TOC Overview

Contents for

QuickPak Professional for Windows Help

About QuickPak Professional

Functions & Subroutines

Introduction

Array Routines

DOS Services

Functions

Menu/Input Routines

Keyboard Routines

Miscellaneous Routines

String Manipulation Routines

Windows Specific Routines

Custom Controls, Properties and Events


Contents Overview

QuickPak Professional for Windows:

Functions and Subroutines


Contact Information
Contact Information:

Crescent Software, Inc.

11 Bailey Avenue

Ridgefield, CT  06877-4505

TEL:  203.438.5300

FAX:  203.431.4626

CIS:  70662,2605

Toll Free Sales:  1.800.35.BASIC


Volume 1 Contents

Welcome to QuickPak Professional for Windows

Overview

QuickPak Professional is Easy to Use

License Agreement

Installation

Quick Start

Dynamic Link Libraries

Modules, Forms, and Project Files

Using QuickPak Professional DLL Routines

Passing Values to Subprograms and Functions

Passing Parameters with ByVal

Numeric Parameters

String Parameters

Passing Fixed-Length String Arrays

Using the QuickPak Professional for Windows File Access Routines

Opening Files

Sequential Files

Random Files

Saving Arrays to Disk

Bit Arrays

Sorts vs. Indexed Sorts

Global Key Processing In Windows

Hanlding Hot Keys

Monitoring Keystrokes

3DFX

Using 3DFX

Colors

3DFX and Video Resolution

The 3DFX Demonstration Program

Welcome to the Crescent FormWizard

Using the FormWizard

Creating a flat file form

Creating a sub form

Integrating the Form into your projects

How the FormWizard Works

Relational Forms

Visual Basic Tips and Techniques

Controlling the Size of your .EXE Programs

Using Ampersands in Controls

Calling Controls on other Forms

Passing Controls as Paramaters to other Procedures

Passing Visual Basic Function Results to Routines

Passing Properties to Procedures

Limiting the Character Width of a Text Control

Implementing Over-Strike Mode for Text Controls

Technical Support

Credits


Introduction Section
Introduction

Thank you for purchasing QuickPak Professional for Windows!  We have put every effort into making this the finest and most complete collection of Visual Basic utilities available.  We sincerely hope that you find it both useful and informative.  If you have a comment, a complaint, or perhaps a suggestion for another product you'd like to see, please let us know.  We want to be your favorite software company. Before we begin discussing the contents of the QuickPak Professional for Windows utilities, please take a few moments to fill out the enclosed registration card.  Doing this entitles you to free technical support by phone, as well as ensuring that you are notified of possible upgrades and new products.  All upgrades are offered at very reasonable costs, but we can't tell you about them unless we know who you are!

Please note that if you purchased this product directly from us, you do not need to register as we have already done this for you. Also, please mark the product serial number on your disk labels. License agreements and registration forms have an irritating way of becoming lost, and doing this will ensure that the number is always handy if you need to contact us.

You may also want to note the version number in a convenient location, since it is stored directly on the distribution disk in the volume label.  If you ever have occasion to call us for assistance, we will probably need to know the version you are using.  To determine the version number for any Crescent product, simply display a directory of the original disk.  The first thing that appears will be something like:

Volume in drive A is QP4WIN 3.XX

We are constantly improving all of our products, so you may want to call us eriodically and ask for the current version number.  Major upgrades are always announced, however minor fixes or additions are generally not.  If you are having any problems at all-even if you are sure it is not with one of our products-please call us.  We support all versions of Microsoft Basic, and can often provide better assistance than Microsoft.


Welcome to QPak for Win

Welcome

To QuickPak Professional For Windows

QuickPak Professional for Windows is a comprehensive collection of subroutines, functions, and custom controls designed to complement Microsoft Visual Basic.  There are three key components in QuickPak Professional:

Assembly language routines contained in a dynamic link library that provide a dramatic improvement over what would be possible using Visual Basic alone.  Some of the routines improve on Visual Basic's speed, while others allow access to DOS and windows services not possible any other way.  Assembler routines can also eliminate the need to use On Error.  In addition, many of the DOS BASIC keywords supported in other versions of Microsoft BASIC, but not in Visual Basic, are reinstated.

Visual Basic subprograms and functions to perform a variety of chores that would be tedious or difficult to write yourself.  Many of these routines provide examples of Windows programming techniques that are not shown in either the Visual Basic documentation or even the Windows SDK (Software Development Kit).  These examples can be invaluable if you are new to Visual Basic or Windows programming in general.

Custom controls that provide enhanced versions of existing Visual Basic controls, as well as some completely new ones to allow for a more flexible user interface.

Commented source code is provided for all the included routines and controls-both Visual Basic and assembler-not only to show how they are used, but to explain how they work as well.


Overview

QuickPak Professional for Windows contains many different and varied utilities which are intended to be added to programs you write in Visual Basic.  Several hundred routines are provided to search and sort arrays, manipulate strings, and interact with DOS and Windows in ways not possible using Visual Basic alone.

In addition to the standard “toolbox” services common to other packages, we have also provided a number of additional utilities. For example, the Evaluate function can be used to compute the result of almost any numeric expression at runtime.  There are also routines for performing date and time arithmetic very quickly, as well as a complete set of financial and statistical functions similar to those found in most popular spreadsheet programs.

In addition, QuickPak Professional for Windows comes with routines to search, find, and encrypt files, and sort an array or file on any number of keys.

Finally, we have provided several Visual Basic custom controls that let you create flexible user interfaces for your programs.  These include sophisticated data entry controls, an enhanced list box, scroll bars that report changes immediately, a clock with an alarm option, a calendar control, common dialogs, and form controls.

There are many other programs and utilities included, and only by examining this manual in detail will you become acquainted with all of them. Most of the routines are amply illustrated by accompanying demonstration programs and forms, and a file is provided that contains Declare statements and Type definitions for each assembler routine.


QPak is Easy to Use
QuickPak Professional for Windows

Is Easy to Use

In designing QuickPak Professional for Windows, major effort has gone into making the routines as easy to use as possible, without sacrificing any of their power, flexibility, or speed.

For example, the number of passed parameters has been kept to the absolute minimum, which helps to reduce the size of your programs. Further, all of the DOS services that require a drive letter will accept either upper or lower case, or a null string to indicate the default drive.

In addition, the routines that read a list of file names and lists of window handles are designed to read all of them in one operation.

Where other methods require you to loop repeatedly getting one at a time until an error occurs, the QuickPak Professional routines process entire lists in a single call.  Where appropriate, entire variable length string arrays can be processed, without forcing you to use Type arrays.

Finally, QuickPak Professional for Windows employs functions where appropriate.  In many cases this will greatly simplify their use. For example, to obtain the name of Window's own directory is as easy as:

Calculator$ = WinDir$() + "\CALC.EXE"

Contrast that with the usual method:

BufSize% = 144

Directory$ = Space$(BufSize%)

BufSize% = GetWindowsDirectory(Directory$, BufSize%)

Directory$ = Left$(Directory$, BufSize%)

Calculator$ = Directory$ + "\CALC.EXE"

Clearly, the QuickPak Professional method is easier, and contributes less code as well.


License Agreement

You are licensed to distribute any application you create using our routines, as long as you do so as an executable program only.  You may also distribute any of the dynamic link libraries (.DLL, .VBX) contained in this package.

You are not licensed to distribute any Crescent Software source code, or any Crescent Software routines in such a way that other people can reuse them.  Further, if you create a custom DLL that contains any of Crescent Software routines, you must use the same techniques we do to prevent the resultant DLL from being run within the Visual Basic environment.

See Also:

Technical Support


Installation

Disk #1 contains the SETUP.EXE installation program.  This is a Visual Basic application that must be run from Windows.  In addition, the Visual Basic runtime library, VBRUN200.DLL, must be accessible. That is, it must be in one of the Windows directories (WIN or SYSTEM), or in one of the directories listed in your path.  If you do not have Visual Basic or for any other reason, you can install the included .ZIP files manually from the DOS command line by running the PKUNZIP.EXE program supplied on disk #1.

To start the installation program, select Run from the program manager's File menu, then type A:SETUP.  SETUP.EXE will display a list of .ZIP files to be installed along with default destination drives and directories.  The defaults may be changed to suit your own preferences.  Each .ZIP file has a check box to its left indicating whether or not it will be installed.  When you are satisfied with the destination drives and directories, press the button titled Start Installation.  If for any reason you want to quit the setup program, press the Cancel button.  During installation, you will be prompted for additional disks as needed.

The .ZIP files on the distribution disk contain demonstration programs with all of the relevant .MAK, .FRM, and .BAS files (DEMOSRC.ZIP).  In addition, a master demonstration program QP4WIN is included.  The demonstration ties all the individual demonstration programs together with a menu bar, which makes it easy to locate the demonstration for each routine.  Note that this master demo is huge, and can take quite a while to load into from Visual Basic.  Therefore, you may want to use the individual .MAK files instead.

The DLLS.ZIP file contains the Dynamic Link Libraries, which hold all of the assembler routines and runtime code for the supplied custom controls.  We suggest that you install these into the Windows SYSTEM directory.

The WIN31DLL.ZIP file contains an updated version of the SOUND.DRV sound driver supplied with Windows version 3.0 and the Common Dialog libraries.  If you intend to use the Play function supplied with this package in Windows 3.0 or the Common Dialog Controls, you should install this file into the Windows SYSTEM directory.  Doing this will overwrite the original driver with the updated version.  If you have a newer version of Windows the WIN31DLL.ZIP file will not be marked for installation, and it should not be used.   The remaining .ZIP files, ASMSRC1.ZIP and CTRLSRC.ZIP, contain the assembly language and C source code for all the routines contained in the dynamic link libraries.  Note that you do not need to access the source code to use QuickPak Professional for Windows, and it is provided solely for people who are interested in learning how these routines were written.


Quick Start

This manual covers the many important topics you will need to know to use QuickPak Professional effectively.  Besides providing a list of each routine and its calling syntax, many other details are described in depth.  In addition, a number of tutorials on various aspects of Visual Basic programming are also contained in the section entitled “Visual Basic Tips and Techniques”.  If you are just beginning to use Visual Basic you should skip ahead to the next chapter.

If you are familiar with Visual Basic programming and want to begin right away, all you have to do is to declare the routines you want to use and make the appropriate calls.  The correct Declare and Call syntax for every routine is provided in the reference section of this manual. The PRODECL.BAS file contains Declare statements for each routine, and these can be pasted into your own programs.  We recommend that you paste only the Declare statements that you need, because all declarations and constants are added to Visual Basic executable files even if they are not used.  One way to do this is to load PRODECL.BAS into the Windows Notepad program, and then use the clipboard to copy and paste the text between Notepad and your program's global module.  We have also provided a program SUMMARY.EXE that can be used to look up and paste any constant, Type definition or declare statement into your programs.

To start using our custom controls you need only add the appropriate .VBX file to your project.  Use the File and Add File... menu commands to do this.


Dynamic Link Libraries Overview
Dynamic Link Libraries

In Visual Basic, most external subprograms and functions are stored in files called Dynamic Link Libraries (DLLs).  A DLL is similar to both the Quick Libraries and BRUN libraries that are used by regular compiled BASIC.  That is, routines in a DLL are external to your program, and they are loaded into memory when your program is run. However, the DLL system of library management has a few advantages over uickBASIC's .QLB and .LIB system.

For instance, unlike QuickBASIC, Visual Basic loads routines from a DLL only as it needs them.  Further, if Visual Basic requires the use of a single routine in a DLL, it pulls only that routine out of the DLL at runtime and executes it.  The DOS linker, on the other hand, adds every routine called by your program to the .EXE file.  When you run your program, everything gets loaded into RAM even if it's not used right away.  (Although BASIC 7 PDS can use overlay files to get around this).

Perhaps the biggest advantage of DLLs is that any Windows program can call any routine in a DLL.  This makes it possible for several programs to share the same library without having to load several copies into memory at once.  In a multi-tasking environment, this type of code sharing is essential to help minimize memory usage.  All versions of Microsoft's BASIC compilers have similar capabilities in the form of the BRUN libraries.  These have the advantage that in a multi-program application, the BASIC runtime library routines are loaded into memory only once.  This allows faster execution between programs, and reduces the amount of disk space each program needs.

While you might think that having to distribute extra files with your program is a nuisance, the advantages of DLLs under Windows for your users far outweigh the inconvenience for the programmer. When you attempt to call any procedure contained in this file, a search is performed for the supplied QPRO.LIC file.  If the file cannot be found, a warning message is displayed and the load is aborted.  The search order for  QPRO.LIC is as follows:

1) Visual Basic's directory (Directory containing VB.EXE).

2) The Windows or Windows System directory.

3) All directories listed in the user's path.

The INSTALL.EXE program copies the QPRO.LIC file along with the QPRO200.DLL file to the same directory.  If you accept the default destination directory for this file, it will be installed into the Windows System directory and will be easily found by QPRO200.DLL.  If it was installed anywhere other than the directories listed above, QPRO200.DLL may not be able to load.


Modules, Forms, and Project Files

Visual Basic stores code and program-related data in a much different way than conventional BASIC compilers.  For instance, with QuickBASIC your code can reside in one or more modules, and in one or more SUB or FUNCTION procedures.  In addition to residing in the Main, Global, and other modules, Visual Basic procedures can be created in one or more Forms (.FRM files).

A Module is a file that contains Visual Basic source code, and usually has a .BAS file extension.  Visual Basic programs sometimes include a global module which contains declare statements and other data definitions.  This is where you will place Declare statements for routines contained in our dynamic link libraries.

One of the biggest advantages that using modules offers is easy  expansion to your programs.  You may add routines that you (or we) have already written, tested, and debugged, and simply call them from anywhere in your program.  This also lets you reuse the same routine in more than one program; therefore, it reduces the amount of programming effort needed.  The QuickPak Professional for Windows package provides many Visual Basic modules that you can add to your application by simply using the File and Add File... menu commands.

A form is a Visual Basic term for a window.  There are many procedures built-in to every form, and they are named according to a specific event or action that occurs while the program is running.  For instance, every form has a procedure called Form_Load which automatically executes when the form is loaded.  Form_Load, like all of the other built-in routines, contains no code unless you put it there.  These merely identify the correct “place” to put code that you want to execute when the form is loaded, such as variable initializations.  Some other built-in procedures are Command1_Click, which executes every time the command button #1 is clicked on, and VScroll1_Change, which executes whenever the value of the Vertical Scroll Bar #1 has been changed.

A project file is simply a file which contains all the modules and form file names that a program uses, and it refers to all the pieces that make up a Visual Basic program.  When you start a new program, you will be creating a new project file.


Using QuickPak Professional DLL Routines

There are only a few things you need to do before you can use the QuickPak routines.  The first step is to make sure that all the QuickPak Professional DLL and .VBX files are either in your Windows SYSTEM directory, or a directory listed in your PATH.  If you followed the installation procedure, they should already be in your Windows SYSTEM directory.  You must have only one copy of each DLL on your hard disk.  If you also have other copies in other directories be sure to delete them.

One limitation of Visual Basic is the way in which Declare statements are accessed.  Visual Basic does not support the QuickBASIC '$INCLUDE metacommand which allows you to load a Declare text file (as opposed to declaring each routine by hand).  Visual Basic instead requires that you place all your DLL declares in a module called the Global module or the general section of a form or module.  This is less convenient for two reasons.

First, you must hand copy every routine from your Declare file using the Clipboard, Windows Notepad, or another Windows text editor. Although this is usually not a big task, it is not as easy as using the '$INCLUDE statement that QuickBASIC offers.

Secondly, whereas QuickBASIC uses Declare statements only as a guide to know how to compile your program, Visual Basic actually adds all of the Declare statements in the Global module to your .EXE file regardless of whether or not they are used.  This makes it impractical to copy (or $INCLUDE if it were possible) the entire Declare file into the Global Module.

The method of declaring routines that we recommend is to use the supplied SUMMARY.EXE program, and keep it minimized where you can get at it quickly.  Then, as you add QuickPak Professional routines to your program you can call it up and copy one or more Declare statements into your module.  Once a routine is declared in the Global Module you can use it anywhere in your program.

Nearly all of the routines in QuickPak Professional expect integer arguments.  The easiest way to insure that integers are passed is to use DefInt A-Z as the very first line of each module.  This tells VB that all variables starting with the letters A through Z (that is, all variables) are to be treated as integers unless otherwise specified.

Please note that many programmers mistakenly assume that placing DefInt A-Z in their Global module causes it to affect all modules. This is not the case, and you should thus add a separate DefInt to each module in your project.

If you do not use DefInt A-Z, Visual Basic assumes that all variables are variants.  If you need to have variables of a different type, you may append the appropriate type suffix to those variables to override the integer default.  Table 1 shows each type suffix that may be used to ensure a variable's data type:

TABLE 1

Standard Visual Basic Variable Type Suffixes

X%  =  3	'Integer

X&  =  098657	'Long Integer

X!   =    14.2                         'Single Precision

X#  =  173.9	'Double Precision

X@  =  1234567.89	'Currency

X$   =  Hello”	'String

There are several advantages to using integer variables whenever possible.  First, operations on integers are many times faster than equivalent operations on single or double precision numbers or variants.  Second, integers require less memory since they only take two bytes of memory; single and double precision variables occupy four and eight bytes of memory  respectively.  In addition, if you declare an assembly language DLL routine that expects an integer value with any other type of variable, you are certain to get wrong results.  In some cases, you could also lock up the PC requiring a reboot.  Before you add a QuickPak Professional routine to your program you must determine whether the routine is written in Basic or assembly language.  This is clearly documented at the top of the page that describes each routine.  If you are adding an assembler routine, you can access the routine with a call, as shown in the routine's description.

If you are adding a BASIC routine, you must use the Add File command from the Visual Basic File menu to add the correct module to your project.  Then you can call the routine as shown in the routine's description.


Passing Values to Subprograms and Functions

The Declare and Call syntax for the QuickPak Professional for Windows routines is listed in each routine's description in the reference section of this manual.  However, there are some interesting features of Visual Basic worth mentioning here.

Arguments that are passed to external programs do not always need to be variable names.  You may pass a constant value to an external routine just as you can pass a variable.  The two examples shown in Figure 1 demonstrate this.

Example 1:

Month% = 12

Print MonthName$(Month%)

or

Print MonthName$(12)

Example 2:

X$ = "Encrypt Me"

Password$ = "My Password"

Encrypt X$, Password$

or

Encrypt X$, "My Password"

Figure 1:  Passing Variables And Constants to a Procedure


Passing Parameters With ByVal

When you specify a numeric variable as an argument to a subprogram, Visual Basic sends the address of that variable.  This is the most efficient method in every case except one: when you are passing an integer value to a function that does not alter the variable.

See Also:

Numeric Parameters  String Parameters  Passing Fixed-Length String, Arrays   Eliminating On Error


Numeric Parameters
Passing Parameters With ByVal

Numeric Parameters

Integers occupy only two bytes of memory and passing them by value requires handling only those two bytes.  But the absolute address of any variable is four bytes-two bytes for the segment, and two for the address within that segment.  Therefore, passing a variable by address (also called passing by reference) requires handling four bytes.

You may ask, “Why not just pass the value of an integer, and save two bytes every time?”.  If the argument is being read by the called procedure and is not modified to also return information, then sending the value is indeed appropriate.  But when information is returned in a passed parameter the called routine must know the variable's address, so it can assign the new value.  Therefore, you should use ByVal only for integer variables, and only when information is not being returned by the procedure.  The Declare statements in the reference portion of this manual show where and when ByVal should be used.

See Also:

Passing Parameters with ByVal


String Parameters
Passing Parameters With ByVal

String Parameters

Normally, when you pass a conventional (variable-length) string to an external subprogram or function, Visual Basic actually passes a four-byte handle to the string.  The receiving routine must then call an internal Visual Basic routine to actually get at the data represented by the handle.  While this is fine for routines that know how to deal with Visual Basic string handles, most DLL routines written for other languages cannot use this method.  Those routines are usually designed to expect a segmented address (far pointer) to a string that is terminated by a character zero.  This type of string is often referred to as an ASCIIZ string.

In order to let Visual Basic programs pass strings to DLL routines written in other languages, Microsoft modified the behavior of the ByVal keyword when used with strings.  If you used ByVal for a string parameter in a Declare statement, Visual Basic uses a different method when you later call the routine.  First, a copy of the string is created with a Chr$(0) zero byte appended to it, and then the segmented address of the copy is passed to the routine.  When the routine returns to the calling program Visual Basic copies the temporary string back into the original variable, in case the routine has changed it.

If this sounds like a lot of work, it is.  In general, DLL routines that process Visual Basic strings directly are considerably faster than those that expect ASCIIZ strings.  Most of the routines contained in QuickPak Professional accept Visual Basic strings directly; however, there are some exceptions.  When a QuickPak Professional for Windows routine must pass a string to DOS, using ByVal makes a lot of sense.  Since DOS requires that they be in the ASCIIZ format, it is just as effective to let Visual Basic do the work rather than add the code to create an ASCIIZ string to the QuickPak routines.

The following is an example of declaring a string using ByVal:

Declare Function Exist% Lib "QPRO.DLL" (ByVal FileName$)


Passing Fixed-Length String Arrays
Passing Parameters With ByVal

Passing Fixed-Length String Arrays

All the routines in QuickPak Professional for Windows that operate on arrays are designed to receive the address of a starting element of the array.  This is usually done with code such as the following:

AddInt IntArray%(Start), Num2Add%, NumEls%

Here, IntArray%(Start) is the first element to consider, and AddInt can assume that all the subsequent elements lie beyond it in a contiguous block of memory.  This would also be true of a routine that accepts a variable length string array.  But instead of the data itself being contiguous, the four byte string handles are stored consecutively in memory.

With fixed-length string arrays, however, Visual Basic does not pass the address of the data; it instead passes a string handle of a copy of the array element.  That is, Visual Basic first copies the data contained in the fixed-length string to a temporary variable length string, and then passes the handle of that string to the called routine.  Since the handle to the temporary copy has no relation to the actual ixed-length string array, there is no way for an external routine to determine where the array's data resides.  Unfortunately, this precludes passing fixed length string arrays to any of the QuickPak Professional for Windows array processing routines.

One way around this problem is to create a Type array that includes a fixed-length string component as follows:

      '--- Define a Type structure to hold file names

      Type FileNames

         FileName As String * 12

      End Type

      '--- Declare the routine that reads file names into an array

      Declare Sub ReadFileT Lib "QPRO.DLL" (ByVal Spec$, Array As FileNames)

Next, you would dimension an array of fixed-length strings using that Type structure in your procedure:

      ReDim Array(1 To 100) As FileNames

When you want to pass the array to a subprogram you would do this:

      ReadFileT "*.*", Array(1)

Remember that this workaround applies only when passing an entire fixed-length string array.  You can pass a single element of a fixed-length string array to a routine that expects a variable-length string with no problem.  The following is a perfectly legal call:

Declare Function Null% Lib "QPRO.DLL" (Work$)

Dim Work As String * 50

  ...

  ...

Empty% = Null%(Work)


Eliminating On Error

Many of the DOS assembly language routines provided with QuickPak Professional may at first appear to be duplicates of functions already built in to the Visual Basic Language.  For example, we provide routines to create and remove directories, delete and rename files, plus many other services that VB can already do directly.

Unfortunately, there are several problems with the way that DOS errors are handled by Visual Basic.  With the introduction of QuickBASIC 4.0, error handling has become even more complicated and convoluted. It seems to us that if you attempt to, say, open a file for input that isn't there, your program should be able to know that right away. Having to first specify where to go to if an error occurs is both non-intuitive and inconvenient.  Visual Basic allows you to use the statement On Error Resume Next, which causes execution to continue at the next line after an error occurs.  But having to use this statement frequently in every procedure is not very convenient.

Because of the problems with On Error we have provided a number of DOS and BIOS routines to bypass Visual Basic altogether, and manipulate disk files directly.  In all cases these routines are called with arguments similar to those in Visual Basic.

However, rather than use On Error to direct your program to an error handler, you may simply query the new reserved variable, DOSError and WhichError.

Of course, an external toolbox package can't really add new reserved words to Visual Basic.  Rather, these are integer functions that report whether or not an error occurred on the last DOS operation, and if so which one.  Because these are designed as functions, they must be declared before they can be used.  (See the section entitled “Using QuickPak Pro DLL Routines” elsewhere in this documentation for more information on using DLL functions.)

This method lets you quickly determine if an error has occurred, without having to constantly specify new locations for error handling in each portion of your program.  For example, testing if a file was successfully deleted after using the KillFile routine is a simple as this:

Declare Sub KillFile Lib "QPRO.DLL" (ByVal FileName$)

Declare Function ErrorMsg$ Lib "QPRO.DLL" (ByVal ErrorNumber%)

Declare Function WhichError% Lib "QPRO.DLL" ()

...

...

KillFile FileName$

If WhichError%() Then

  MsgBox ErrorMsg$(WhichError%()), 48

Else

  MsgBox FileName$ + " was deleted.", 64

End If


Using The

QuickPak Professional for Windows File Access Routines

One of BASIC's few quirks is the way that disk and other errors are handled.  Unlike other languages that let you test the success or failure of the most recent operation, Visual Basic instead requires On Error.  It is up to you to first establish a block of code that will receive control when an error occurs, and then figure out what happened and where you were before the error occurred.

QuickPak Professional for Windows lets you avoid On Error by including replacement file access routines that return an error code.  After any operation that may result in an error, you can simply query the QuickPak Professional for Windows DOSError and WhichError functions.  The short program fragment shows this in context:

      CDir "\QB45"

            If DosError%() Then

                Print "Unable to change to the \QB45 directory"

           End If

All of the QuickPak Professional for Windows routines operate this way, although some routines return additional error information in their passed parameters.  The DOSError function merely tells if an error occurred, and WhichError lets you determine which one.  WhichError returns the same codes that Visual Basic does for the same situations.

See Also:

Opening Files  Sequential Files  Random Files   Saving Arrays to Disk


Opening Files
Using The

QuickPak Professional for Windows File Access Routines

Opening Files

QuickPak Professional for Windows includes a complete set of routines that can optionally replace Visual Basic's file handling statements.  Which you use depends on your needs, and there are advantages and disadvantages to both.  If you are writing a small program that will occasionally be used, using On Error makes a lot of sense.  But for programs where performance is critical, using the QuickPak Professional for Windows replacements will probably be worth the added effort.

Before a file can be accessed by using either method, it must first be opened.  Unlike Visual Basic's Open statement that accepts an argument to specify the access mode (Input, Output, Append, Binary, or Random), the QuickPak Professional for Windows routines operate in binary mode only.  We do include routines for inputting lines of sequential text and accessing fixed-length records.  However, from the perspective of DOS, all file access is purely binary.  When you use Visual Basic's Open, it is Visual Basic that remembers the mode, and it is Visual Basic that prevents you from using Line Input with a random access file.  The QuickPak Professional for Windows file routines make no such distinction, and this added flexibility is yet another reason to use them.

Unlike Visual Basic's Open, the QuickPak Professional for Windows FOpen family of routines will not create a file if it does not exist.  If you plan to read from an existing file only, then FOpen can be used in the same way as Visual Basic's Open For Input.  Likewise, if you plan to write to a file that already exists-if it currently has a length of zero-then FOpen will also work as expected.  However, if you need to create a file, you must first use FCreate.  You can use the Exist function to determine if a file already exists.

Like Visual Basic's Open For Output, FCreate will create a file if it does not exist, or truncate it to a length of zero if it does.  There are two additional Open replacement routines: FOpenS and FOpenAll.  FOpenS (the S stands for Shared) is intended for use in a network application, and lets you read and write data to disk while allowing others read access only.  FOpenAll is similar, except it lets you specify all of the possible sharing options.

The three FOpen routines are similar in that they expect the name of the file and a Handle parameter.  With Visual Basic's Open you make up a file number, then use the number whenever you need to access the file.  Contrast that with the method used by QuickPak Professional for Windows, where the file number is returned to you.  Please understand that this is the same method that DOS uses, and indeed, all other high-level languages.  It is important to understand that the Handle is returned to you by these routines, and you should never assign it yourself.

After you have called one of the FOpen routines, you will check to see if an error has occurred.  For example, an error is possible if the diskette drive door is open, or if the file to be opened was not found.  You will use the DOSError and WhichError functions to detect errors, as shown below.

If Not Exist%(FileName$) Then	'no such file

  FCreate FileName$	'so create it

End If

FOpen FileName$, Handle%	'then open it

If DOSError%() Then	'check for an error

  Beep	'sound an alarm

  MsgBox ErrorMsg$(WhichError%())	'print an error message

  Exit Sub	'bail out

End If

DOSError would detect an error if it had occurred during the opening process.  Then, the WhichError% and ErrorMsg$ functions can be used to display an appropriate message.  WhichError% returns an error number, and ErrorMsg$ returns an equivalent string.  For example, if you attempt to open a file that does not exist WhichError% will return 53, and ErrorMsg$ will return “File not found”.  If no error occurred, you would continue the program and either read or write data to the file.

See Also:

Sequential Files	Random Files


Sequntial Files
Using The

QuickPak Professional for Windows File Access Routines

Sequential Files

Normally when you save a sequential file in Visual Basic, you use either the Write# or Print# statements.  These statements write a string to disk, followed by carriage return and line feed characters. These characters later tell Input or Line Input that they have encountered the end of a line.  With QuickPak Professional for Windows you instead use the FPut family of subroutines to write sequential data to disk. FPut was designed to write as many bytes to disk as are in the string being written.

This is ideal for binary files, and with only slightly more work, FPut can also be used for sequential files as well.  Since FPut does not automatically add a carriage return and line feed to the end of the string that you are saving, you must add these manually:

Source$ = "Crescent Software"

CRLF$ = Chr$(13) + Chr$(10)

FPut Handle%, Source$ + CRLF$

...	'write other data here

...

FClose Handle%	'then close the file

To retrieve sequential data from a file with Visual Basic you would normally use the Line Input # command.  With QuickPak Professional for Windows you will instead use FLInput.  FLInput is similar to Visual Basic's Line Input statement, except that it too will set an error flag that may be detected with DOSError should an error occur while you are reading.  You must also create a temporary buffer string to hold the data as it is being read from disk.

To read data from a file you might first check to see if the file name is valid, using the Exist function as before.  Then you would open the file and read the data.

If Not Exist%(FileName$) Then

  MsgBox "File not found"

  Exit Sub

End If

FOpen FileName$, Handle%	'open the file

Buffer$ = Space$(82)	'create a file buffer

Work$ = FLInput$(Handle%, Buffer$)	'Work$ receives data

...

...

FLInput will read data into Buffer$ until it finds a Chr$(13) line terminator, a Chr$(26) end of file mark, or until Buffer$ is filled.

See Also:

Opening Files   Random Files


Random Files
Using The

QuickPak Professional for Windows File Access Routines

Random Files

Visual Basic uses the Get and Put commands for accessing random data files with fixed-length records.  Get and Put can work in different ways, depending upon the mode in which the file was opened, and which optional arguments were used with Put and Get.  QuickPak Professional for Windows offers several replacement routines, with each designed for a specific use.

QuickPak Professional for Windows replaces Get and Put with FPut, FPutR FPutRT, FPutRTA, and FPutT, and their counterparts FGet, FGetR, FGetRT, FGetRTA, and FGetT.  The routines containing an “R” (random) as part of the suffix will access data in a file at a designated record number.  If the suffix contains a “T” (Type) then the routine is intended for use with Type.  If the suffix also includes an “A”, the routine will read or write all or part of a Type array.

Finally, it is worth mentioning that with random files in Visual Basic, the first record number is 1, and the first byte in the file is also considered to be byte 1.  With QuickPak Professional for Windows, while the first record in a random file is still 1, the first byte in a binary file is byte 0.  This is important to understand when pecifying a file offset with FSeek.

See Also:

Opening Files   Sequential Files


Saving Arrays to Disk
Using The

QuickPak Professional for Windows File Access Routines

Saving Arrays to Disk

The Visual Basic language is very similar to QuickBASIC, with a few important exceptions.  One of these is the lack of support for BLOAD and BSAVE.  We can understand that in the hands of an inexperienced programmer, BLOAD and BSAVE could completely crash your system if used incorrectly.  However, there are times when having them can be quite helpful.

We have included the QBLoad and QBSave from the DOS version of QuickPak professional, and these work the same way as their QuickBASIC counterparts including error trapping.

One very important application for using QBSave is to reduce the time needed to save a numeric array to a large data file.  When an entire array of numeric data must be saved on a disk, the most common approach is to create a sequential file and use the Print # statement to write each value.

Unfortunately, using a sequential file this way is undesirably slow, because each number must be converted from Visual Basic's internal format into a string of ASCII digits.  Worse, each number takes up extra space on disk to store the digits.  That is, the integer value 12345 will occupy six bytes, with one required for each digit and another for the separating comma that is also needed.  Contrast this with Visual Basic's method for storing an integer in memory, where only two bytes are used regardless of the variable's value.

Using QBSave overcomes both of these problems, by quickly transferring the contents of an entire numeric array to a disk file.  Because the file is simply a “snapshot” of memory, the operation happens very quickly without regard for the meaning of the values in the array. The QuickPak Professional FPutA routine is equally useful for saving an area of memory, and it is described in the reference section of this manual.

Though an entire numeric array may be saved in this fashion, you cannot save a conventional string array.  The elements in a string array are not in contiguous memory locations; rather, they are scattered throughout the 64K string space allocated to each module.  A table of handles is used to find their actual location.

The QuickPak array saving and loading routines can also be used with Type arrays, but with one exception: string components with the Type definition must have an explicit length.  The first example below uses a variable-length string within the Type structure, and the second uses a fixed-length string.  Only the second Type definition can be used.

Type VLType

  X As Integer

  Y As Long

  Z As String	'this is a variable-length string

End Type

Type VLType

  X As Integer

  Y As Long

  Z As String * 10	'this is a fixed-length string

End Type

Determining the number of bytes a given array occupies is quite easy to do, based on the number of elements it contains and the size of each element.  Each element in an integer array comprises two bytes, while a long integer or single precision variables require 4.  Double precision and Currency values need eight bytes each, though a Type variable may of course be any size.  Based on this information, the number of bytes to specify for any single-dimension array may be computed as follows:

NumBytes& = (UBound(Array) - LBound(Array) + 1) * ElSize

Where ElSize is either two, four, eight, or the length of each Type element.

It is very important to dimension an array that is to be loaded again later, prior to calling QBLoad.  Following a similar logic, you would determine the number of elements needed by first using the QuickPak Professional FileSize function to obtain the size of the file.  Next you subtract the seven bytes taken by the QBSave header, since that portion of the file isn't actually loaded.  Finally, divide the result by the size of each element.

In truth, you don't really need to subtract the seven byte header, since dimensioning to more elements than needed won't cause any harm. However, failing to dimension an array large enough is guaranteed to cause problems.

One additional topic worth mentioning here is the use of a QBSave header which is part of the file being saved.  When you create a file using QBSave, it creates a seven byte header immediately before the saved data.  This header contains a special signature byte, as well as other information about the file.  Note that the QBSave file format is the same as BASIC's BSAVE file format.

The very first byte in a QBSave file is &HFD, which merely identifies it as having been created by QBSave.  The next four bytes hold the segment and address from which the data has been saved respectively. The final byte holds the length of the data in the file, not including the seven byte header.  Notice that the QuickPak Professional QBLoad command ignores this header, and simply loads the entire file into memory at the segment and address you give it.

See Also:

Opening Files  Sequential Files  Random Files   Array Routines


Bit Arrays

Before the introduction of QuickPak Professional the smallest type of variable you could use in Visual Basic was an integer.  In cases where a program needs the range of values offered by integers using them makes a lot of sense.  But when the values are very small and there are a lot of them, integers can waste a considerable amount of memory.  One effective solution is to create a byte array by defining a string variable with a length equal to the number of elements that are needed:

X$ = STRING$(Size%, 0)

Each element (character) can hold a value between 0 and 255, and with only a little extra work, you can coerce the range to be from -128 to 127.  But often all that is really needed is a simple True or False type variable, and even using a byte for each value is wasteful.

Three routines are provided to manipulate what we call Bit arrays. Like byte arrays, a string variable is used for the actual storage. However, each character in the string really represents eight separate elements.  Thus, creating a Bit array with 1000 elements will occupy only 125 bytes, as opposed to the 2000 bytes that an integer array requires.  At sixteen to one this represents a substantial savings indeed!

The two Bit routines that assign and retrieve array elements are written in assembly language, and a Basic subprogram is provided to dimension the array.  Because an assembler routine cannot easily create strings or change their length, this part must be done in Visual Basic.  Of course, creating the string is very simple and you could do it just as easily with the formula shown below:

Array$ = String$((NumEls% \ 8) + 1, 0)

SetBit and GetBit may then be used to set and get the various elements as they are needed.

See Also:

The DimBits Routine


Sort vs. Indexed Sorts
Sorts vs. Indexed Sorts

Many different types of sorts are provided in the QuickPak Professional for Windows package, however all of the sorts fall into one of two categories. The “plain” sorts are intended to order all of the elements in an array to be either ascending or descending.  However, we have also provided a set of indexed sorts that instead sort a table of pointers. Let's take a closer look at what this means.

When one of the normal sorts is called to sort an array (or a portion of an array), the elements are actually exchanged to fall into the desired order. If you print all of the elements in sequence after the array has been sorted, you can easily see how they have been arranged:

SortI  Array%(Start), Size%, Direction%

For X = Start To Size%

  Print Array%(X)

Next

The indexed sorts, on the other hand, sort a parallel integer array rather than the primary array, thus retaining the original order while also providing access in sorted order. The example below shows the same array being sorted as above, except the elements are printed out by referring to the index array subscripts.

        ISortI A%(Start), I%(0), Size%, Direction%

            For X = Start To Size%

               Print A%(I%(X))

            Next

It is important to understand that you must dimension the parallel index array to the correct number of elements. Further, it is also up to you to initialize the index array to increasing values beginning with zero. The indexed sorts require a reference point to be able to access each element in the primary array being considered. This means that when the sorting begins, index element zero must contain a 0, element one will hold a 1, and so forth.

Even though it would be simple for the sorting routine to initialize the index array itself, this would disallow resorting a second time based on another key. Therefore, we have provided the routine InitInt which is pecifically intended to initialize an integer array to ascending values very quickly. The example below shows how InitInt would be used in the typical context of sorting an entire array:

Dim A%(1000), I%(1000)	'dim the arrays

InitInt I%(1), 1, 1000	'initialize the index

ISortI A%(0), I%(0), 1001, 0

For X = 0 to 1000

  Print A%(I%(X))

Next

One final point worth mentioning concerns the QuickPak Professional for Windows assembler sorts. We have provided a number of different sort routines, each optimized for a particular type of variable. That is, separate sorts are included for integer arrays, long integers, single precision, double precision, normal strings, and fixed-length or Type arrays.

If you intend to sort, say, only long integers, then that routine will provide the highest performance possible. However, if you need to sort several different types of arrays in one program, you might consider using only the Type sort routine. (Or the indexed Type sort where applicable.)  The Type sort can be used to sort all of the other variable types, except conventional string arrays.

See Also:

Array Routines


Global Key Processing in Windows

One of the limitations of Visual Basic is that it doesn't provide a way to define “hot keys” that let your programs detect and act on events when they are not running in the foreground.  Regular DOS applications can use TSR (Terminate and Stay Resident) programs to detect special keys or events.  For example, a TSR could be written to insert the current date at the current cursor location when using any text editor.  Although Windows allows multiple programs to be resident all at once this is only half of the solution, because Visual Basic provides no direct way to trap keyboard events from other applications.

To solve this problem, QuickPak Professional for Windows includes two sets of functions: CreateHK and KillHK,  CreateAK and KillAK.  These routines let your Visual Basic programs insert hooks into the Windows keyboard event chain, to receive control when keys are pressed¾even if the program is minimized or does not have the focus.

See Also:

Handling Hot Keys   Monitoring Keystrokes


Handling Hot Keys
Global Key Processing in Windows

Handling Hot Keys

CreateAK (All Keys) allows your program to monitor all keystrokes typed into any running application, and KillAK closes the keyboard hook.  CreateHK (Hot Key) lets you specify a specific key you want to act on, and KillHK reverses the process when you are finished.  The syntax for creating a hot key is as follows:

hHotKey& = CreateHK&(VKCode%, Shift%, Wnd, UserVK%)

Here, VKCode is any valid virtual key code.  These are the same codes used in the KeyDown and KeyUp event procedures.  The values for the different keys can be found in the CONSTANT.TXT file that comes with Visual Basic.  All of these constants have the “KEY” prefix.

The Shift parameter is the mask for the shift keys you want included in your hot key.  This value is the same as the Shift parameter used in the Visual Basic KeyDown and KeyUp events, and the numbers may be added together to specify multiple shift keys.  For example, you could define a hot key as Shift-Ctrl-P.  Defining Shift mask values is explained in the Visual Basic Programmer's Guide in the section entitled Writing Low Level Keyboard Handlers”.

Wnd is the hWnd property of the Form that you want to receive hot key events, and UserVK is the value you want sent to your window when your hot key comes up.  This can be any value you want; however, it is best to use values greater than 144 because that is where the normal key codes end.

hHotKey is returned by the function as the handle to the hot key. This value is used only to disable the hot key later on.  Note that hHotKey is returned with a value of -1 if the hot key you selected is already in use, or -2 if no more hot keys are available for use. (The hot key handler is limited to 1024 simultaneous hot keys.)  Also note that hot keys defined in the Windows Macro Recorder are independent of CreateHK.  Please understand that CreateHK cannot detect if a Macro Recorder hot key is already taken by any other Windows application, so be careful of overlap!

Once you have defined as many hot keys as you want, you then need to create a KeyDown event handler for that form.  Since a KeyDown event is rarely used for a form, the hot key handler can easily coexist with normal KeyDown processing as long as you define UserVK codes that do not represent valid virtual key codes.

The KeyDown handler uses two parameters: KeyCode and Shift.  The only one of interest to you is KeyCode, because the event will be triggered only if all the proper shift keys are pressed.  When you do receive control the KeyCode parameter will contain the UserVK code that you specified when creating your hot key.  You can then use a Select Case block to process the different hot keys that you have created.

When you unload your form, you must call KillHK and pass the handle (hHotKey) returned to you by CreatHK.  Not only will this let other applications use those hot keys, but the hot key handler does a lot of cleanup when a hot key is eliminated so key processing in other programs is as fast as possible.  When you onsider that the hot key handler has to filter every keystroke, it is imperative to processes keys as fast as possible.

Also, both CreateHK and KillHK are documented in the reference section of this manual in the section “Keyboard Routines”.  We have also provided a more complete demonstration program HOTKEY.MAK which when compiled to an executable allows you to use Visual Basic macros that we have already defined.  Included is a macro that sends to Visual Basic a sequence of key strokes to save a source module as text, and then reload it, in order to remove wasted space as mentioned earlier.

The following Form File example registers a hot key (Ctrl-F) with the QuickPak Professional for Windows hot key handler.  When the hot key is pressed later, this program will send the string “Declare Function” to the application that currently has focus.

DefInt A-Z

Declare Function CreateHK& Lib "QPRO.Dll" (ByVal VKCode%, ByVal Mask%, ByVal Wnd%, ByVal UserVK%)

Declare Sub KillHK Lib "QPRO.Dll" (ByVal hHotKey&)

Const CTRL_F = 200

Const CTRL_MASK = 2

Dim HK1&

Sub Form_Load ()

  HK1& = CreateHK&(Asc("F"), CTRL_MASK, hWnd, CTRL_F)

  If HK1& = 0 Then

    MsgBox "Unable to install the hot key handler."

    End

  End If

End Sub

Sub Form_KeyDown (KeyCode As Integer, Shift As Integer)

  Select Case KeyCode

    Case CTRL_F

SendKeys "Declare Function "

    Case Else

  End Select

End Sub

Sub Form_Unload (Cancel As Integer)

  KillHK HK1&

End Sub

Here we first declare the CreateHK and KillHK routines in the General section of the form.  Next, two constants are defined¾the first, CTRL_F, is the number to be passed to the form's KeyDown event procedure when the hot key is pressed.  The second constant is CTRL-MASK, and this is the shift mask value that specifies the Ctrl key.  Finally, a module level variable named HK1& is defined to hold the hot key's registration handle that is returned by CreateHK.

In order to register the hot key you will use the form's Load procedure.  Here CreateHK is called passing the ASCII value of the key, followed by the CTRL_MASK shift mask value defined earlier.  The third parameter is simply the window handle of the form obtained using the Visual Basic .hWnd property.  The last parameter specifies the KeyCode value to be passed to the form's KeyDown event procedure when the hot key is pressed.

Once the hot key is registered, control will be passed to the form's KeyDown procedure whenever the user presses the hot key.  This happens regardless of which form or application currently has the focus. Within this procedure, you simply test the KeyCode parameter to see if it has the value specified earlier (CTRL_F) when it was first registered.  If it does, we use the Visual Basic SendKeys method to pass key strokes on to the application that currently has focus.

Note that an explicit Test for the specified hot key is not always necessary, because a form's KeyDown procedure is never called unless there are no controls contained within it. The last procedure, Form_Unload, is invoked by Visual Basic whenever the form is closed or unloaded.  This is where you will call KillHK to remove the hot key from the Windows event chain.  To remove a hot key simply pass the hot key handle that was returned by CreateHK earlier.

See Also:

Global Key Processing In Windows   Monitoring Keystrokes


Monitoring Keystrokes
Global Key Processing in Windows

Monitoring Keystrokes

If you want to monitor system-wide keyboard activity you can use the CreateAK (Create All Key handler) and KillAK routines to set up a key handling form that works in a manner similar to the hot key handlers described in the previous section.  You might use these to create a macro recorder as a companion to the hot key routines shown in that section.  The syntax for invoking CreateAK is as follows:

Flag% = CreateAK%(hWnd)

Here, you simply pass the form's window handle, and the function returns this same handle (in Flag%) if it was successful.  If CreateAK has been used before and KillAK was not used to remove the key handler, CreateAK instead returns -1 to indicate that there is already an active key handler for this form. Once CreateAK has been called, your form's KeyDown event procedure will be activated whenever any key has been typed into any application.  At this point you can interpret the KeyDown subprogram's two parameters¾KeyCode% and Shift%¾in the same manner you normally would for this type of event.  When your application is unloaded, or when you are through processing keys, you should call KillAK to remove the keyboard handler from memory:

KillAK hWnd

Here, hWnd is the window handle of your form, and after KillAK returns the keyboard handler will be uninstalled if no other forms are currently using it.  Both CreateAK and KillAK are documented in the reference portion of this manual in the section “Keyboard Routines”.

The following Form File example records all of the keys that are typed into whichever application has the focus.  When this form is unloaded it saves the recorded keys to a binary file named MACRO.BIN.

Global Module:

DefInt A-Z

Declare Function CreateAK% Lib "QPRO.Dll" (ByVal Wnd%)

Declare Sub KillAK Lib "QPRO.Dll" (ByVal Wnd%)

Declare Sub FPutAH Lib "QPRO.DLL" (ByVal FileName$, _

  Array As Any, ByVal ElSize%, ByVal NumEls%)

Type KeyVals

  KeyCode As Integer

  Shift As Integer

End Type

Form File Code:

DefInt A-Z

Dim K(1 To 500) As KeyVals

Dim NumKeys%

Sub Form_Load ()

  If CreateAK%(Form.hWnd) = 0 Then

    MsgBox "Unable to install the keyboard handler."

    End

  End If

End Sub

Sub Form_KeyDown (KeyCode As Integer, Shift As Integer)

  If NumKeys% << UBound(K) Then

    NumKeys% = NumKeys% + 1

    K(NumKeys%).KeyCode = KeyCode

 	    K(NumKeys%).Shift = Shift

  End If

End Sub

Sub Form_Unload (Cancel As Integer)

  KillAK hWnd

  FPutAH "MACRO.BIN", K(1), 4, NumKeys%)

End Sub

In the project's Global Module you will declare the two keyboard routines, CreateAK and KillAK, along with the QuickPak Professional for Windows FPutAH array saving routine.  Next, a Type structure is defined to hold the values passed to the form's KeyDown procedure in the KeyCode and Shift parameters.

In the form's General section an array named K() is defined that can hold up to 500 keystrokes typed by the user.  Finally, a global variable is defined to track the number of keystrokes recorded.

To start receiving keystrokes call CreateAK from your Form_Load procedure.  Once this function returns successfully keystrokes will begin to be passed to the Form_KeyDown procedure.  Here, the values of the two parameters, KeyCode and Shift, are saved to the K() array that was defined earlier.

When the form is closed (unloaded) the Form_Unload procedure is called, and here the KillAK routine is called to unload the keyboard handler.  Finally, FPutAH is called to save the entire K() array to the disk file.

See Also:

Handling Hot Keys   Global Key Processing In Windows


3DFX

3DFX is a collection of Visual Basic routines that you can use in your projects to create various 3D effects.  The effects include 3D boxes that can be displayed with any of the following styles:

Raised Box

Sunken Box

Raised Line Box

Sunken Line Box

Shadowed Box

Beveled-edge Box

Boxes may be drawn at any specified location, or automatically drawn around existing Visual Basic controls.  You can specify an offset distance that the box will be drawn inside or outside the control, and you may specify the pixel width of the box line.  3DFX also lets you create:

3D Lines in either Raised Line or Sunken Line Styles

3D Labels, either raised, sunken, or embossed in any font style

You can also create simulated 3D frames using boxed Picture Boxes as the container object. Styles include the regular box styles.  In addition, the caption can be left, center or right-justified, and displayed using the Normal, Raised, or Sunken effects.

You can also do pattern fills using one of several pre-defined patterns, or with a custom pattern you specify at run-time.

You can also create 3D inset option buttons using regular buttons in combination with overlaid picture boxes.

The images are copied to the picture at run-time from a small bitmap (3DFX.BMP) which also is provided.

In addition, you can also create 3D check boxes using regular check boxes in combination with overlaid picture boxes.  The technique is similar to that used for the 3D option buttons.  The check boxes can be created using the following styles:

Sunken

Raised

Sunken diamond

Raised diamond

The type of check mark used with any of these box types can be one of the following:

small black “x”

larger, thinner “x”

blue check mark

red check mark

3D Picture Command Buttons can be created using the shape control and the image control.  The simulated button highlight is drawn using the BoxControlOnForm routine.  The simulated pressing and depressing of the button are done in code.  An example is included in the 3DFXDEMO.FRM file.

The main goal of these routines is to provide a useful set of 3D functions in straight Visual Basic code.  There are a few Windows API calls in the pattern fill routines, but for the most part these routines are written entirely in Basic.  The advantage is that you don't need to distribute a custom .VBX or .DLL file just to draw a few boxes, or create a 3D label.

See Also:

Using 3DFX    Colors    3DFX and Video Resolution    The 3DFX Demonstration Programs


Using 3DFX
3DFX

Using 3DFX

In order to use the 3DFX routines in your program you will need to load the 3DFX.BAS module.  Use the Add File command from the File menu and select 3DFX.BAS.  No other forms or modules are required.  If you will be using the 3D option buttons or check boxes you will also need to load the supplied bitmap (3DFX.BMP) into a non-visible picture box on one of your forms.  Each routine has a comments section which explains how the parameters are used, and provides a sample of the routine in action.  You must always call Initialize3DFX when your program begins.  You might also want to set the global color variables, but if you don't, a default “gray scale” color scheme will be used.

You can call the 3D effect routines in either the form_load or the form_paint subs of your form.  If you don't need to worry about any other graphical events disturbing the 3D effects it is best to use the form_load procedure.  Just be sure to set “me.autoredraw=true” before calling the 3DFX routines, and then set “me.autoredraw=false” after calling the routines.  A good practice is to create a common routine called DoEffects() which does all 3D-graphical effects.  You can then call this routine in form_load or at any other time when the 3D look of your form needs to be changed.  Look in either FRM3DFX.FRM or FRMPATFL.FRM of the 3DFXDEMO.MAK sample application for examples of this technique.

You can also use the form_paint event if it is necessary to refresh the 3D-effects after other graphical routines.  This works well, but can result in more overhead as the 3D effects are re-generated whenever a portion of the screen is invalidated.

Putting the routines in form_load and saving the autoredraw image makes subsequent displays quicker and more uniform, but at the cost of memory and system resources.  If a screen is used relatively infrequently, or if it is shown and then unloaded, just use the form_paint event.

HINTS:

Rather than using the BoxDrawForm function directly, which requires you to fill in a RECT structure with height, width, top, and left numbers, instead place a lightweight “shape” control on the form and size it the way you want it interactively.  Make the shape control's color and border the same as the form's color. Then use the BoxControlOnForm function to create the box.  This function simply draws the selected box style around the invisible shape control.  The same technique can be used with the 3D line controls. First place a transparent line in the correct position on the screen, and then call the LineOnForm function by passing it the x1, y1, x2, y2 values of the line control.

See Also:

Colors   3DFX and Video Resolution Demo Programs   The 3DFX Demonstration Program


Colors
3DFX

Colors

The 3DFX routines are primarily intended to be used on forms that have a light gray background color.  However, you can use any color scheme you like.  If you want to use the default gray color scheme you don't have to set any global variables.  If you wish to use another color scheme, however, you will need to set the global variables Box_Highlight, Box_Background, and Box_Dimmed in your form_load routine.  Experiment with various color combinations on the FRMCOLOR.FRM screen.  Because the 3D option buttons and check boxes use pre-defined bitmaps, they will only look right on a “gray scale” application.

See Also:

Using 3DFX   3DFX and Video Resolution   The 3DFX Demo Programs


3DFX and Video Resolution
3DFX

3DFX and Video Resolution

Some of the routines require that you specify numbers in terms of pixels.  These are ACTUAL pixels when running in a system with a TwipsPerPixel ratio of 15 to 1.  This includes VGA and SuperVGA with the “small fonts” option.  In other TwipsPerPixel ratios (ordinarily 12 on systems using the “large fonts” option) the pixels are scaled by a factor of 1.25.  You design for a ratio of 15, and 3DFX automatically adjusts for a ratio of 12.

See Also:

Using 3DFX    Colors    The 3DFX Demonstration Program


The 3DFXDemo Program
3DFX

The 3DFXDemo Program

In addition to the 3DFX routines themselves, the demo project shows off some new features including the following:

Built-in MDI capability, using arrange, cascade, tile etc.

A “status bar” built by using the 'align to bottom' feature for picture boxes in an MDI Main form

the 'app' collection for app.path, app.previnstance etc.

'me' to refer to the current form

form variables

option explicit

global variables and declarations in any module makes it easy to “plug-in” the 3DFX routines into your program without copying constants to GLOBAL.BAS

uses the “value of the control” for improved efficiency. For example, uses 'text1 = “abc”' rather than 'text1.text = “abc”'parameter types for controls passed to subs are explicit: i.e.: “lbl as label” rather than “lbl as control”use of “KeyPreview” to provide a form-level “hot-key” routine makes use of the new 'hwnd' property for controls used in API calls form set to “ClipControls=false” for improved paint speed use of “screen.TwipsPerPixelX” to determine scaling at run-time Overlapping controls used to create a “picture button” from an ordinary (non-.vbx) command button and a picture box  “zOrder” method to arrange overlapping controls

The demo is also useful because the “Code” menu contains sample code that you can cut and paste into a current project so that you don't have to memorize the function parameters. It also has a little screen that you can use to make up custom patterns for use with the pattern fill routines, and another screen to try out various color combinations.

In order for the code example screens to work, the file 3DFX.BAS must reside in the same directory as the 3DFXDEMO.EXE executable file.

See Also:

Using 3DFX    Colors    3DFX and Video Resolution


Welcome to the Crescent FormWizard

While the new database connectivity in Visual Basic 3.0 makes the use of SQL databases easier than ever, one area that can stand a lot of improvement is the creation of database forms. Currently, you must create a data control with a series of properties, draw enough text boxes on a form for a particular database table, set a series of properties for each of these and even then you're not set up to add new records.

The other limitation is the lack of support for relational forms involving more than one table in a one-to-many relationship. You can easily use our virtual list box (CSVLIST.VBX) to create a read-only relational form but even then you must write code to manage the changes in the relationship.

In MS Access there is a component called FormWizards which takes a great deal of the grunt work out of constructing a database form. Following this model, we have created the Crescent FormWizard for Visual Basic.

The Crescent FormWizard for Visual Basic is a Visual Basic application which enables you to easily generate data entry forms for Visual Basic with just a few mouse clicks. Simply selecting a table and some fields is enough to generate a working data entry screen that uses the new Visual Basic 3.0 data access features.

Don't stop there! You can go on to set a series of layout options for your form, allowing you to design an attractive and consistent look for your application that would otherwise require a tedious process of drawing and property setting. With the FormWizard for Visual Basic you can easily exploit the design properties of our special CSCaption control and our various custom text boxes.

But wait, there's more! Not only have we constructed some sample code to show you how to build a one-to-many form in Visual Basic using our custom text controls (what good is a data aware grid if you don't have full data validation in the grid?!) but we have integrated this technique into our FormWizard so that with a few more clicks of a button your can create a Visual Basic data entry form with an editable subform.

See Also:

Using the FormWizard


Using the FormWizard

Creating  a flat file form

Use the File Open menu to choose the database file (MDB) that contains the table for which you would like a form.  You will then be presented with a list of the tables in that database.  In the case of a dbase database, you will choose an entire directory of DBFs, then you will be presented with a list of the DBFs.  Choose the table by clicking on its name.  Its fields will then be listed.  Choose the fields you wish to include on the form by clicking on their names.  You're done!  You could actually press the Generate button now to create your Visual Basic form.

However, you may wish to use the Design Options dialog box to change the look of the fields, and their field captions.  You can also change the colors of the fields, captions, and the form.  If you wish, you can change the name of any field as it will appear on the form. For example, you might want a field called CustRegNum to display as Customer Registration Number.   Finally, you can also change the text of the title that will appear on the top of the form.

Now you're really done creating your form.  Press the Generate button.  You will first be asked for a form name, and then the name of a project Make File (MAK) to which you desire your form to be added.  If you choose a new Make File, one will be created for you.  If you choose an existing Make File, then your new form, and the necessary VBXs will be added to it.

If the form is attached to a dBase file, then an INI file will be created, matching the name of your project, and it will be placed in your Windows directory.  This INI file is necessary for the data-access engine to find the DLL it uses for Dbase files.  If you distribute your application in compiled form, you will need to distribute the DLL, and this INI file and possibly edit it for your users.  Below are its contents:

[Installable ISAMs]

dBASE III=C:\WINDOWS\SYSTEM\xbs110.dll

dBASE IV=C:\WINDOWS\SYSTEM\xbs110.dll

[dBase ISAM]

Deleted=On

See Also:

Creating a sub form  Integrating the Form into your projects  How the FormWizard Works  Relational Forms


Creating a Sub form
Using the FormWizard

Creating a sub form

To create a sub form,  there is something you need to do before you choose the Create Subform button.  You will be linking one table to another in a one-to-many relationship with a link field  in each table.  Generally, a link field is the primary key of the main table tied to a like named foreign key in the "child" table. Unless the link field in the main form is a counter-field (one that automatically gets a unique serial number), you must include the link field of the main table in your form.  Once you've chosen the fields for the main table, press the SubForm button.  You will again be presented with a list of tables in the database you've chosen.  Choose the table for the Many part of your form.  Choose the fields you wish to include.  If you are going to create an Editable Subform grid, you need to include the field(s) of the primary index so you can give it a unique value when adding records.  You do NOT need to add the linking field.  Now choose how you wish the subrecords to be sorted, and then pick which fields in the “one” and the “many” table will match.  This will be the link field. One is suggested for you based on field names. Finally, choose the type of subfield (read-only or editable), and generate your form.

See Also:

Creating a flat file form   Integrating the Form into your projects  How the FormWizard Works  Relational Forms


Integrating the Form into your Project
Using the FormWizard

Integrating the Form into your Project

You need only add the form you generated to your project, but be sure your project has the following:

CSPict.VBX

CSPictur.VBX  (this one only if you have a picture on your form)

CSText.VBX

CSCapt.VBX

CSCmd.VBX

CSVlistVBX (this one only if you created a read-only subform)

By default, when you select the project in which you want to add your form, the necessary VBXs will be added for you.

See Also:

Using the FormWizard  Creating a flat file form  Creating a sub form How the FormWizard Works Relational Forms


How the FormWizard Works

Flat file forms

There is not too much mystical in the generated flat file databases, but there are a few notes worth mentioning.

1.  Data-aware controls.   The fields are linked, through the DataSource property to a data control that is placed anywhere on the form.  Each field in a flat file has the same DataSource, linking them to a common table, but separate DataField properties, linking them to their database fields.  The data control has a DatabaseName, which is the name of the database on disk, and DataSource, which is the table in the Database.

2. CSPict. This control, placed on a picture box, allows its “virtual” size to be bigger than its on screen size, using scroll bars.  The fields are placed on a picture box with a CSPict to allow you to have more fields than would otherwise fit on the form.  If you want to add any elements to the form, increase the VHeight and VWidth properties of the CSPict control as necessary.

3. CSCaption Control. Each field is placed in a CSCaption control.  This control ensures a consistent relationship between the field-label and the field, while allowing a great variety of user-specified parameters.  You can adjust the properties of this control in a number of ways, but the field will always fit in its caption consistently.

See Also:

Welcome to the Form Wizard


Relational Forms
How the FormWizard Works

Relational Forms

There are two forms of relational forms: with a read-only subform, and with an editable grid.  The read-only is just a flat file database, with its data control, and a special data-aware list box linked to a second datacontrol.  The editable grid type also has a second datacontrol, but, because fields linked to a datacontrol are only connected to one record at a time, data-aware fields can't be used at all.  Instead, a routine call FillFields steps through the subform database, putting the information in the grid.  When the user makes an edit in the grid, a routine called UpData looks to see what row it was made in, what record that belongs to, and makes the changes.  If the change is to a new row, then a new database record gets created.

1. linkingfield_LostFocus

If the linking field is not a counter, then it accepts changes.  If it changes, then we need to store the record so that any added subrecords will be linked to the proper mainrecord, or if it changes to a value for which there are already subrecords, they need to be retrieved.  So its lostfocus routine sees if it has changed, and updates the mainrecord if necessary.

2. The Second Data control

The second data control is not linked to just a table (which would mean all its records) but to a query,  placed in its recordsource, that selects those subrecords that match the main record.  The value to search for is in the hidden field Link.  This extra field is placed on the form in case the user defined a form without a link field, which is allowed if the link field is a counter field.   Each time the mainrecord changes, its Reposition routine rewrites the query for the subrecord control.    That's all that's needed to retrieve the proper rows into the subtable-listbox.   (If the subform is a grid, then the other thing the Reposition routine does is tell the scroll bar how many subrecords there are for the current mainrecord.)

3.  Editable subform

Getting information into the grid is a bit harder than into the listbox.  This is the role of the FillFields routine.  It steps through the sub-database one record at a time, for each row in the grid.  For each record, it gets the text for each field (cycling through the array of field names called “columns”) and places it into the cell for each field using the control array called Ctrl.  The use of this array allows us to place and get text from the fields by cycling through their indices.  Additionally, the bookmark for each record is stored in an array of structures called dta that has a field called bookmarks.  This will allow us to make active any record in the grid remember the second data control actually refers to only one record at a time, even though we show as many as you want.)  If our moving through the records is stopped by hitting an EOF, then the rest of the rows are filled with blanks.  That's how old subrecords are cleared when you move from a mainrecord with more subrecords than the current one.

Because the grid is not hard linked to its datacontrol, making additions or changes has to be done programmatically.  This is done with a pair of routines.  One is placed in each cell in the grid, in its LostFocus event.  Because each column in the grid is an array, we only need a routine for each column.  It takes which column got left, and which row, and passes these to the UpData routine.

The UpData routine first checks to see what control GOT focus after the cell in question got lostfocus.  If it's in the same row, then nothing happens.  Only when the user leaves the row do we bother saving the information, or creating a new record.  Next the routine checks to see if the row has a bookmark, in other words, if it matches a row we know has a record.  Otherwise, the user is creating a record.  Therefore, we add a record to the database, and fill in one of its fields: we know its linking field will have the same value as the link field of the mainrecord.

Information is put in to each field of the database from the grid in the reverse method employed by FillFields.  If the user is editing, not adding, then with the row number we can get the bookmark for the appropriate record from our array of bookmarks, and make the proper record active.  Otherwise we add a record, and store the new bookmark.

With the column and row numbers, we can access the text of any cell in the grid the control array. With the column number, we can get the fieldname from our array of fieldnames.  This is how database fields are accessed.  So we just need to put the cell text into the database.

See Also:

Using the FormWizard Creating a flat file form  Creating a sub form

Integrating the Form into your projects Flat File Forms


Visual Basic Tips and Techniques

Controlling The Size Of Your .EXE Programs

There are many factors that govern the size of executable files created by Visual Basic.  The most obvious is the amount of code in the application; however, there are also less obvious ones that many programmers are not aware of.

Visual Basic does not create true executable files in the same sense that Basic or C compilers do.  That is, when you make an executable file from Visual Basic, what is actually created is a threaded p-code interpreted program that requires the routines contained in VBRUN200.DLL to execute.  Understand that your program is not interpreted in the same manner as the GW-BASIC interpreter that came with early versions of DOS.  Rather, this interpreter is far more sophisticated and newer, faster techniques are used.  Although Visual Basic executable programs are relatively fast, there are several problems with interpreter technologies that you should be aware of.

The size of variable names affects program size.

Unlike compiled programs where variable names are translated to addresses by the compiler, Visual Basic actually includes the name in your program.  The longer the name, the more space that is used.  While we would not recommend using cryptic variable names, you should also try to keep their length to a minimum.

Unlike compiled programs, unused Declare and Constant statements become part of your program.  If you have merged the CONSTANT.TXT file into your global module or left declares there that you are no longer using, the space they occupy is added to the final executable program.  The only solution to this is to manually delete them by first searching for references, and then removing them if none are found.

Like compiled programs, the space occupied by static arrays is added to your executable file.  When you dimension an array in your global module, your program's size will increase by the size of the array.

See Also

Using Ampersands in Controls  Calling Controls on other Forms  Passing Controls as Paramaters to other Procedures

Passing Visual Basic Function Results to Routines  Passing Properties to Procedures

Limiting the Character Width of a Text Control  Implementing Over-Strike Mode for Text Controls


Using Ampersands in Controls
Visual Basic Tips and Techniques

Using Ampersands in Controls

Visual Basic recognizes the ampersand (&) character as a shortcut key identifier.  For instance, if you are creating a menu entry or label caption and you use the ampersand as part of that caption, the letter following the ampersand will be recognized as a shortcut key:

Label1.Caption = "&H" + Hex$(255)

This would display “HFF” and Alt-H would be defined as the shortcut key for the control whose TabIndex is one higher than the label.  If you want the ampersand to be part of the caption, use two of them:

Label1.Caption = "&&H" + Hex$(255)

This would display “&HFF” without a shortcut key.

See Also:

Controlling the Size of your .EXE Programs Calling Controls on other Forms Passing Controls as Paramaters to other Procedures

Passing Visual Basic Function Results to Routines  Passing Properties to Procedures  Limiting the Character Width of a Text Control

Implementing Over-Strike Mode for Text Controls


Calling Controls on other Forms
Visual Basic Tips and Techniques

Calling Controls On Other Forms

Although Visual Basic does not let you call procedures in Form files other than your own, you can invoke a control from another form by using properties that trigger events.  For example, in Form1 you can use this:

Form2.Command1.Value = -1

This will invoke the event procedure Command1_Click contained in Form2.  Another example could be:

Form2.Text1.Text = Form2.Text1.Text

This would invoke the Text1_Change event procedure in Form2 without actually disturbing the control's contents.

See Also:

Controlling the Size of your .EXE Programs  Using Ampersands in Controls  Passing Controls as Paramaters to other Procedures

Passing Visual Basic Function Results to Routines  Passing Properties to Procedures Limiting the Character Width of a Text Control

Implementing Over-Strike Mode for Text Controls


Passing Controls As Parameters to other procedures
Visual Basic Tips and Techniques

Passing Controls As Parameters To Other Procedures

Often overlooked in the Visual Basic manual is how to pass a control to another procedure.  The syntax is as follows:

Sub ClearList (Ctrl as Control)

  For N = 1 To Ctrl.ListCount

   Ctrl.RemoveItem 0

  Next

End Sub

In the calling routine, you could use:

ClearList List1

ClearList List2

You cannot, however, pass a control name from within one of that control's event procedures.  For example, this does not work:

Sub List1_Click

  ClearList List1

  ...

See Also:

Controlling the Size of your .EXE Programs   Using Ampersands in Controls   Calling Controls on other Forms

Passing Visual Basic Function Results to Routines   Passing Properties to Procedures   Limiting the Character Width of a Text Control

Implementing Over-Strike Mode for Text Controls


Passing VB function results to routines declared with As Any
Visual Basic Tips and Techniques

Passing Visual Basic Function Results to Routines Declared with "As Any"

When passing the result of a Visual Basic function directly to a routine, where the parameter is declared “As Any”, Visual Basic will actually pass a variant descriptor, rather than the data type you might expect.  The following declaration uses “As Any” so that any type of data can be passed to the routine:

Declare Sub FPutT Lib "QPRO200.DLL" (ByVal Handle%, Var As Any, NumBytes&)

If we wanted to write a two byte integer to a file representing the length of a string to follow, we might use the following code:

'--- This doesn't work

FPutT Handle%, Len(SomeString$), 2	'Write the string's length

FPut Handle%, SomeString$ 	'Write the actual string data

Under Visual Basic version 1, the Len function would return a two byte integer, as expected, which would be written to the file.  However; under Visual Basic version 2, the Len function actually returns a Variant descriptor and the code above would save only the first two bytes of that structure.

In order to save an actual integer, as intended, you would need to save the string's length into an integer variable and then pass the variable instead of the function result as follows:

'--- This works

Length% = Len(SomeString$)	'Save the length of the string

FPutT Handle%, Length%, 2 	'Write the string's length

FPut Handle%, SomeString$	'Write the actual string data

This is just one example of the potential pitfalls introduced with the Variant data type and the “As Any” clause in Declare statements.  In general, you should assume that all Visual Basic version 2 functions return Variants whose values must be converted to the desired data type, before passing them to routines declared with the “As Any” clause.

See Also:

Controlling the Size of your .EXE Programs   Using Ampersands in Controls   Calling Controls on other Forms

Passing Controls as Paramaters to other Procedures   Passing Properties to Procedures

Limiting the Character Width of a Text Control Implementing Over-Strike Mode for Text Controls


Passing Properties to Procedures
Visual Basic Tips and Techniques

Passing Properties to Procedures

Visual Basic does not let you pass a control property “by reference” to any procedure that expects a normal variable.  That is, a procedure cannot alter the value of a property that is passed as a parameter.  The following code generates a “Parameter type mismatch” error when executed:

AnyProc Text1.Text

 ...

Sub AnyProc (Text$)

To pass an individual property of a control to a procedure that  expects a normal variable, you must either design the procedure to expect the parameter by value using the @ByVal@ keyword, or surround the property with parentheses in the call. The following are two examples:

AnyProc Text1.Text

 ...

Sub AnyProc (ByVal Text$)

or

AnyProc (Text1.Text)

 ...

Sub AnyProc (Text$)

See Also:

Controlling the Size of your .EXE Programs   Using Ampersands in Controls	Calling Controls on other Forms

Passing Controls as Paramaters to other Procedures   Passing Visual Basic Function Results to Routines

Limiting the Character Width of a Text Control  	Implementing Over-Strike Mode for Text Controls

See Also:

Controlling the Size of your .EXE Programs  Using Ampersands in Controls   Calling Controls on other Forms

Passing Controls as Paramaters to other Procedures   Passing Visual Basic Function Results to Routines

Limiting the Character Width of a Text Control   Implementing Over-Strike Mode for Text Controls


Limiting The Character width of a text control

Visual Basic Tips and Techniques

Limiting The Character Width Of A Text Control

It is often necessary to limit the number of characters that can be typed into a data entry field (Text control).  An example would be a data entry form used as a front end for a database.  Most databases require that individual fields be limited to a fixed length, but Visual Basic Text controls allow up to 255 characters to be entered as a single line.  One solution is to use the QuickPak Professional custom edit controls; another is to call the Windows API SendMessage function to tell a normal Text control that it should not accept more than a certain number of characters.  The example that follows shows how to call the Windows SendMessage function:

DefInt A-Z

Declare Function SendMessage% Lib "user" (ByVal hWnd%, ByVal wMsg%, ByVal _

  wParam%, ByVal lParam&)

Declare Function ControlHWnd% Lib "QPRO.DLL" (Ctrl AsControl)

Const WM_USER = &H400

Const EM_LIMITTEXT = WM_USER + 21

Sub Form_Load ()

  Ok& = SendMessage(ControlHWnd(Text1), EM_LIMITTEXT, 32, 0)

  Ok& = SendMessage(ControlHWnd(Text2), EM_LIMITTEXT, 2, 0)

  ...

  ...

Here, you first declare the Windows SendMessage function and the QuickPak Professional for Windows ControlHWnd functions in either the Declarations section of your module or in your Global module.  Next, define two Windows message constants.  The first, WM_USER, is a base number upon which all the other control message constants are built. The second constant is EM_LIMITTEXT, and it is used to notify a Text control that it should accept only a certain number of characters.

In your Form_Load procedure you would place calls to SendMessage to tell each Text control on your form its maximum length.  The first parameter of the call is the window handle of the control, followed by the EM_LIMITTEXT message constant defined earlier.  The third parameter is simply the number of characters you want to allow for the field, and the last parameter is not used so you can specify zero.

After the Form_Load procedure has finished, the user will not be able to enter more characters than were specified in the SendMessage calls for each field.

See Also:

Controlling the Size of your .EXE Programs   Using Ampersands in Controls   Calling Controls on other Forms

Passing Controls as Paramaters to other Procedures   Passing Visual Basic Function Results to Routines

Passing Properties to Procedures   Implementing Over-Strike Mode for Text Controls


Implementing Over-Strike Mode for Text Controls
Visual Basic Tips and Techniques

Implementing Over-Strike Mode For Text Controls

As you probably already know, Visual Basic Text controls do not support over strike mode for data entry.  We can only guess at the reason for this omission, but there is a very simple way to override this default. Place the following code in a Text control's KeyPress event procedure:

Sub Text1_KeyPress (KeyAscii As Integer)

  If KeyAscii >>= 32

    Text1.SelLength = 1

  End If

End Sub

Here, you simply set the selection (highlight) length of the control to 1.  This causes the control to replace the character at the current position with the character that was just typed.  Note the test for an ASCII values of 32 or higher, because some keys such as the Chr$(8) backspace must not be handled this way.  Also note that our custom edit controls fully support over-strike mode including changing the caret shape, with no additional coding on your part.

See Also:

Controlling the Size of your .EXE Programs   Using Ampersands in Controls   Calling Controls on other Forms

Passing Controls as Paramaters to other Procedures   Passing Visual Basic Function Results to Routines

Passing Properties to Procedures   Limiting the Character Width of a Text Control


Credits

A number of people have made important contributions to QuickPak Professional for Windows, and they all deserve credit for their programming skills and hard work.

Jay Munro and Paul Passarelli created all the assembly language subroutines.  In addition, Jay created the CurTime, CSMeter and QPScroll custom controls entirely in assembly language.

Phil Cramer and Don Malin created most of the demonstration programs with additional contributions by Nash Bly, Carl Franklin and Ethan Winer.  Harald Zoschke contributed the calendar demonstration form, and also the CSUNZIP utility.

Carl Franklin created the bitmap manipulation routines.

Jonathan Zuck contributed the QPList custom List Box, the CSForm, CSPict and CSInvis controls, and also designed the custom Scroll Bars and global key handling routines.

Jonathan also desiged the CSCombo, CSOptionList, CSCheckList, CSVList and CSHyterText controls.

The CSOptionList, CSCheckList and CSCombo controls were written by James Shield.

The CSVList control was written by Zane Thomas.

The CSHyperText control was written by Brett Foster.

Chris Schwartz created the six custom edit controls.

Steve Cramp created the enhanced picture control, and the common dialog controls.

Bill Conley created the calendar command and spin controls.

Jim Ferguson contributed the 3DFX routines.

This manual was written by Don Malin and Ethan Winer, with contributions by Carl Franklin.

The documentation was designed and typeset by Jacki Willmott-Pagliaro.


Technical Support

The following information may be of help to you in streamlining your efforts to resolve any technical problems you may have with QuickPak Professional for Windows.

Before you call for Technical Support:

We believe that Crescent Software's technical support staff is second to none because it is comprised of software developers who are available to help you resolve your programming problems, rather than just technical support specialists.  In the event that you discover a bug or an anolmaly, Crescent will fix the problem as soon as possible, and provide a solution in a timely fashion.

However, in order to maximize the effectiveness of your contact with Crescent Software's technical support, we ask that you have the pertinent information available regarding your problem when you call.

Therefore, before you call for tech support, please read through the documentation thoroughly.  We have found that many problems can be solved by applying the information contained in the documentation.  If, after reading through text, you still cannot resolve your problem, then please follow these steps:

GPF? If you are getting a GPF (General Protection Fault), write down the information that is displayed when the error occurs.  Also, make a note of what your code was doing (in general terms.)

ISOLATE IT.  Try to isolate the cause of the error.  If at all possible, step through your code with F8 and F9.  Try to find the one line of code that is causing the error.

SCALE IT DOWN.  If at all possible, try to reproduce the problem in a small test program that you can send in.  Call the technical support line at 203.438.5300 to discuss your question, and possibly arrange the method by which you can send in your test code.

CALL CRESCENT.  You can send in your test program via:

CompuServe Mail:	70662,2605

Crescent BBS:	203.438.3314

FAX:	203.431.4626 (Please keep code to a 2 page minimum)


About QPakPro
QuickPak Professional for Windows is an award-winning  collection of custom controls, subroutines, and functions for Visual Basic.

Included are more than 30 custom controls, 400+ DLL routines, 2.7 MB of sample code, 3+ M of source code and several useful time-saving utilities.

With this kind of unprecedented power, you can quickly create show-stopping Windows -based applications with ease!


Array Routines

AddInt

ArrayISortNum

ArrayISortStr

ArrayISortTyp

ArrayISortVar

ArraySortNum

ArraySortStr

ArraySortTyp

ArraySortVar

CSFindLast

DeleteStr

DeleteT

DimBits

Fill2, 4, 8

Find and Find2

FindB and FindB

FindExact

FindT and FindT

FindTB and FindTB

FindV and FindV

FindVB, FindVB2

GetBit

IMaxC, D, I, L, S

IMinC, D, I, L, S

InitInt

InsertStr

InsertT

ISortC, D, I, L, S

ISortStr and ISortStr

ISortT and ISortT

ISortV and ISortV

KeySort

MaxC, D, I, L, S

MinC, D, I, L, S

Search

SearchT and SearchT

SetBit

SortC, D, I, L, S

SortStr and SortStr

SortT and SortT

SortV and SortV


AddInt

assembler subroutine contained in QPRO200.DLL

Purpose:

AddInt adds a constant value to all of the elements in a specified portion of an integer array.

Declare Syntax:

Declare Sub AddInt Lib "QPRO200.DLL" (IntArray%, ByVal Num2Add%,ByVal NumEls%)

Call Syntax:

AddInt IntArray%(Start%), Num2Add%, NumEls%

Where:

Array%(Start%) is the first element in the array that is to be modified, Num2Add% is the value to be added to each element, and NumEls% is the number of elements to be processed.

Comments:

AddInt is useful in conjunction with the various indexed sort routines. Because the indexed sorts have no way to determine which portion of an array is actually being sorted, the index array is always returned holding values ranging from zero to the number of elements minus one. AddInt allows you to quickly adjust the sorted index values to reflect the correct element numbers in the primary array.

Values may also be subtracted from each element by simply using a negative value in Num2Add%.

Example Location:

Project File	Form/BASIC File	Procedure

NARRROPS.MAK	NARRROPS.FRM	Add_Click


ArrayISortNum

assembler subroutine contained in QPRO200.DLL

Purpose:

ArrayISortNum sorts a parallel index array related to all or part of a huge (64K) single or multi-dimensional numeric array in either ascending or descending order.

Declare Syntax:

Declare Sub ArrayISortNum Lib "QPRO200.DLL" (Index%(), Array() As Any, _

FirstEl As Any, ByVal NumEls%, ByVal Direction%, ByVal MemberSize%) *

Call Syntax:

ArrayISortNum Index%(), Array(), Array(Start), NumEls%, Direction%, MemberSize%

Where:

Index() is the parallel array of element numbers. **

Array() is the array to sort.

Array(Start) is the first element to include in the sort. ***

NumEls% is the number of elements to include in the sort.

Direction% is zero for an ascending sort, or nonzero to sort descending.

MemberSize% determines the type of data to consider. ****

Comments:

The ArraySort family of routines are replacements for the SortX and ISortX routines. If you are using Visual Basic 2.0 or higher with a huge array and the array element size is either 16384 or not an even power of 2, then we strongly suggest using the ArraySort routines. Otherwise, use the SortX and ISortX routines, as they are generally faster (but are limited to arrays whose element size is not 16384 or an even power of 2).

* Although the Numeric routines indicate  Array() As Any  and FirstEl As Any, these definitions must be one of the five numeric types (i.e. Currency, Double, Integer, Long, and Single.)

** Although Index() should usually be an Integer array, it is also possible to utilize a user defined Type array. However, the first member of the index type MUST be an Integer.  This is an example of an Index Type:

Type ArrayISortIndexType

 	  '-- The Integer index must be first!

 	  MyIndex As Integer

  '-- One or more user variables may follow:

  MyData1 As Single

  MyData2 As String

End Type

*** The ArraySort routines will sort a multi-dimensional array by the first dimension. For example, an array might be represented as Array(X,Y), in which case the consecutive elements are Array(X + 1, Y), Array(X + 2, Y), Array(X + 3, Y), etc.

**** Internal comparisons differ not only in the amount of data they must compare, but also the method of comparison.  Acceptable values may be selected from the following set:

-1 Integer

-2 Long integer

-3 Single

-4 Double

-5 Currency

-6 Fuzzy single

-7 Fuzzy double

Fuzzy data types are supported for completeness. However, we do not recommend that you use them for sorting because of their nature. If you sort an array with fuzzy logic, some numbers may be out of order due to the nature of a fuzzy search.

On Indexed Sorts:

In contrast to a standard array sort routine in which the data in the array is re-arranged, Index sorts leave the original array intact and instead sort an array of incremented integer values (1, 2, 3, 4, etc). After sorting, the index array holds the element numbers of the original array in a sorted order.

For example:

Before the sort:

Index (1) = 1 	Array(1) = 16

Index (2) = 2 	Array(2) = 23

Index (3) = 3 	Array(3) = 7

After the sort:

Array() stays the same

Index(1) = 3 because Array(3) = 7	i.e. lowest value

Index(2) = 1 because Array(1) = 16	i.e. middle value

Index(3) = 2 because Array(2) = 23	i.e. highest value

Before you call an indexed sort routine, you must initialize the index array with incrementing integer values. You can use the QuickPak Professional for Windows InitInt routine to do this.

For example, imagine you have an integer array called Array%(1 to 100) which contains random values. The following code will perform an indexed sort and display the values in sorted order, while keeping the original array intact.

'-- Create an index array

ReDim IndexArray%(1 to 100)

'-- Set the values to be 1, 2, 3,...100

Call InitInt (IndexArray%(1), 1, 100)

'-- Set up the variables that are passed to ArrayISortNum

NumEls% = 100

Direction% = 0

MemberSize% = -1	'-- see data type chart above (-1 = Integer)

'-- Sort the index array based on the contents of Array%()

Call ArrayISortNum (IndexArray%(), Array%(), Array%(1), NumEls%,

Direction%, MemberSize%)

'-- Display the original array elements in sorted order.

For i% = 1 to 100

 	   NextElement% = IndexArray%(i%)

 	   NextValue% = Array%(NextElement%)

 	   Debug.Print NextValue%

Next

See Also:  	ArrayISortStr, ArrayISortTyp, ArrayISortVar, ArraySortNum

ArraySortStr, ArraySortTyp, ArraySortVar


ArrayISortStr

assembler subroutine contained in QPRO200.DLL

Purpose:

ArrayISortStr sorts a parallel index array related to all or part of a huge (64K) single or multi-dimensional string array in either ascending or descending order.

Declare Syntax:

Declare Sub ArrayISortStr Lib "QPRO200.DLL" (Index%(), Array() As String, _

FirstEl As String, ByVal NumEls%, ByVal Direction%, ByVal CaseSensitive%)

Call Syntax:

ArrayISortStr Index%(), Array$(), Array$(Start), NumEls, Direction%, CaseSensitive%

Where:

Index() is the parallel array of element numbers. *

Array$() is the array to sort.

Array$(Start) is the first element to include in the sort. **

NumEls% is the number of elements to include in the sort.

Direction% is zero for an ascending sort, or nonzero to sort descending.

CaseSensitive% is zero to ignore, or nonzero to honor capitalization.

Comments:

The ArraySort family of routines are replacements for the SortX and ISortX routines. If you are using Visual Basic 2.0 or higher with a huge array and the array element size is either 16384 or not an even power of 2, then we strongly suggest using the ArraySort routines. Otherwise, use the SortX and ISortX routines, as they are generally faster (but are limited to arrays whose element size is not 16384 or an even power of 2).

* Although Index() should usually be an Integer array, it is also possible to utilize a user defined Type array. However, the first member of the index type MUST be an Integer.  This is an example of an Index Type:

Type ArrayISortIndexType

   '-- The Integer index must be first!

   MyIndex As Integer

   '-- One or more user variables may follow:

   MyData1 As Single

   MyData2 As String

End Type

** The ArraySort routines will sort a multi-dimensional array by the first dimension. For example, an array might be represented as Array(X,Y), in which case the consecutive elements are Array(X + 1, Y), Array(X + 2, Y), Array(X + 3, Y), etc.

On Indexed Sorts:

In contrast to a standard array sort routine in which the data in the array is re-arranged, Index sorts leave the original array intact and instead sort an array of incremented integer values (1, 2, 3, 4, etc). The sorted index array holds the element numbers of the original array in a sorted order.

For example:

     Before the sort:

Index (1) = 1 	 Array(1) = 16

Index (2) = 2	 Array(2) = 23

Index (3) = 3	 Array(3) = 7

     After the sort:

     Array() stays the same

  Index(1) = 3	 because Array(3) = 7	i.e. lowest value

  Index(2) = 1	 because Array(1) = 16	i.e. middle value

  Index(3) = 2	 because Array(2) = 23	i.e. highest value

Before you call an indexed sort routine, you must initialize the index array with incrementing integer values. You can use the QuickPak Professional for Windows InitInt routine to do this.

For example, imagine you have an integer array called Array%(1 to 100) which contains random values. The following code will perform an indexed sort and display the values in sorted order, while keeping the original array intact.

'-- Create an index array

ReDim IndexArray%(1 to 100)

'-- Set the values to be 1, 2, 3,...100

Call InitInt (IndexArray%(1), 1, 100)

'-- Set up the variables that are passed to ArrayISortNum

NumEls% = 100

Direction% = 0

MemberSize% = -1 	                         '-- see data type chart above (-1 = Integer)

'-- Sort the index array based on the contents of Array%()

Call ArrayISortNum (IndexArray%(), Array%(), Array%(1), NumEls%,

Direction%, MemberSize%)

'-- Display the original array elements in sorted order.

For i% = 1 to 100

 	   NextElement% = IndexArray%(i%)

 	   NextValue% = Array%(NextElement%)

 	   Debug.Print NextValue%

Next

See Also:  	ArrayISortNum, ArrayISortTyp, ArrayISortVar, ArraySortNum

ArraySortStr, ArraySortTyp, ArraySortVar


ArrayISortTyp

assembler subroutine contained in QPRO200.DLL

Purpose:

ArrayISortType sorts a parallel index array related to all or part of a huge (64K) single or multi-dimensional array of any type in either ascending or descending order.

Declare Syntax:

Declare Sub ArrayISortTyp Lib "QPRO200.DLL" (Index%(), Array() As Any, _

     FirstEl As Any, ByVal NumEls%, ByVal Direction%, ByVal ElSize%, _

     ByVal MemberOffset%, ByVal MemberSize%, ByVal CaseSensitive%)

Call Syntax:

ArrayISortTyp Index%(), Array(), Array(Start), NumEls%, Direction%, ElSize%, _

     MemberOffset%, MemberSize%, CaseSensitive%

Where:

Index() is the parallel array of element numbers. *

Array() is the array to sort.

Array(Start) is the first element to include in the sort. **

NumEls% is the number of elements to include in the sort.

Direction% is zero for an ascending sort, or nonzero to sort descending.

ElSize% is the size in bytes of each element in the Type.  It is most easily passed as Len(Array(Start)). ***

MemberOffset% is the offset into the user defined Type of the key member to consider for the sort.  For a fixed length string array use zero.

MemberSize% determines the type of data to consider.

CaseSensitive% is zero to ignore, or nonzero to honor capitalization.

Comments:

The ArraySort family of routines are replacements for the SortX and ISortX routines. If you are using Visual Basic 2.0 or higher with a huge array and the array element size is either 16384 or not an even power of 2, then we strongly suggest using the ArraySort routines. Otherwise, use the SortX and ISortX routines, as they are generally faster (but are limited to arrays whose element size is not 16384 or an even power of 2).

* Although Index() should usually be an Integer array, it is also possible to utilize a user defined Type array. However, the first member of the index type MUST be an Integer.  This is an example of an Index Type:

Type ArrayISortIndexType

 	  '-- The Integer index must be first!

 	  MyIndex As Integer

  '-- One or more user variables may follow:

  MyData1 As Single

  MyData2 As String

End Type

** The ArraySort routines will sort a multi-dimensional array by the first dimension. For example, an array might be represented as Array(X,Y), in which case the consecutive elements are Array(X + 1, Y), Array(X + 2, Y), Array(X + 3, Y), etc.

*** Internal comparisons differ not only in the amount of data they must compare, but also the method of comparison.  Acceptable values may be selected from the following set:

+n	Fixed length string of n bytes

- 1	Integer

-2 	Long integer

-3 	Single

-4 	Double

-5 	Currency

-6 	Fuzzy single

-7 	Fuzzy double

-8 	Variant

-9	String (variable length)

Fuzzy data types are supported for completeness. However, we do not recommend that you use them for sorting because of their nature. If you sort an array with fuzzy logic, some numbers may be out of order due to the nature of a fuzzy search.

On Variant arrays and Variants in Types:

Since the Type sorts will accept the Variant MemberSize and the Variant Sorts do not require a variant type identifier, the FirstEl argument is used to determine the type of comparison used for sorting.  The variant data types are listed here for your reference:

VT_EMPTY

VT_NULL

VT_I2

VT_I4

VT_R4

VT_R8

VT_I8

VT_DATE

VT_STRING

NOTE: VT_EMPTY and VT_NULL variant data types do not indicate the type of sort desired. If Array(Start) is of either type the sort will not work. When VT_EMPTY and/or VT_NULL appear in other elements within the sort range of the array the sort will work, in which case a VT_EMPTY will be considered less than a VT_NULL and both are ordered before any actual data in the array.

On Indexed Sorts:

In contrast to a standard array sort routine in which the data in the array is re-arranged, Index sorts leave the original array intact and instead sort an array of incremented integer values (1, 2, 3, 4, etc). The sorted index array holds the element numbers of the original array in a sorted order.

For example:

     Before the sort:

  Index (1) = 1	 Array(1) = 16

  Index (2) = 2	 Array(2) = 23

  Index (3) = 3	 Array(3) = 7

    After the sort:

     Array() stays the same

Index(1) = 3	 because Array(3) = 7	i.e.lowest value

Index(2) = 1	 because Array(1) = 16	i.e. middle value

Index(3) = 2	 because Array(2) = 23	i.e. highest value

Before you call an indexed sort routine, you must initialize the index array with incrementing integer values. You can use the QuickPak Professional for Windows InitInt routine to do this.

For example, imagine you have an integer array called Array%(1 to 100) which contains random values. The following code will perform an indexed sort and display the values in sorted order, while keeping the original array intact.

'-- Create an index array

ReDim IndexArray%(1 to 100)

'-- Set the values to be 1, 2, 3,...100

Call InitInt (IndexArray%(1), 1, 100

'-- Set up the variables that are passed to ArrayISortNum

NumEls% = 100

Direction% = 0

MemberSize% = -1 	     '-- see data type chart above (-1 = Integer)

'-- Sort the index array based on the contents of Array%()

Call ArrayISortNum (IndexArray%(), Array%(), Array%(1), NumEls%,

Direction%, MemberSize%)

'-- Display the original array elements in sorted order.

For i% = 1 to 100

   NextElement% = IndexArray%(i%)

           	NextValue% = Array%(NextElement%)

          	 Debug.Print NextValue%

Next

See Also:

ArrayISortNum, ArrayISortStr, ArrayISortVar, ArraySortNum

ArraySortStr, ArraySortTyp, ArraySortVar


ArrayISortVar

assembler subroutine contained in QPRO200.DLL

Purpose:

ArrayISortVar sorts a parallel index array related to all or part of a huge (64K) single or multi-dimensional variant array in either ascending or descending order.

Declare Syntax:

Declare Sub ArrayISortVar Lib "QPRO200.DLL" (Index%(), Array() As Variant, _

        FirstEl As Variant, ByVal NumEls%, ByVal Direction%, ByVal CaseSensitive%)

Call Syntax:

ArrayISortVar Index%(), Array(), Array(Start), NumEls%, Direction%, CaseSensitive%

Where:

Index() is the parallel array of element numbers. *

Array() is the array to sort.

Array(Start) is the first element to include in the sort. **

NumEls% is the number of elements to include in the sort.

Direction% is zero for an ascending sort, or nonzero to sort descending.

CaseSensitive% is zero to ignore, or nonzero to honor capitalization.

Comments:

The ArraySort family of routines are replacements for the SortX and ISortX routines. If you are using Visual Basic 2.0 or higher with a huge array and the array element size is either 16384 or not an even power of 2, then we strongly suggest using the ArraySort routines. Otherwise, use the SortX and ISortX routines, as they are generally faster (but are limited to arrays whose element size is not 16384 or an even power of 2).

* Although Index() should usually be an Integer array, it is also possible to utilize a user defined Type array. However, the first member of the index type MUST be an Integer.  This is an example of an Index Type:

Type ArrayISortIndexType

     '-- The Integer index must be first!

     MyIndex As Integer

     '-- One or more user variables may follow:

     MyData1 As Single

     MyData2 As String

End Type

** The ArraySort routines will sort a multi-dimensional array by the first dimension. For example, an array might be represented as Array(X,Y), in which case the consecutive elements are Array(X + 1, Y), Array(X + 2, Y), Array(X + 3, Y), etc.

On Variant arrays and Variants in Types:

Since the Type sorts will accept the Variant MemberSize and the Variant Sorts do not require a variant type identifier, the FirstEl argument is used to determine the type of comparison used for sorting.  The variant data types are listed here for your reference:

VT_EMPTY

VT_NULL

VT_I2

VT_I4

VT_R4

VT_R8

VT_I8

VT_DATE

VT_STRING

NOTE: VT_EMPTY and VT_NULL variant data types do not indicate the type of sort desired. If Array(Start) is of either type the sort will not work. When VT_EMPTY and/or VT_NULL appear in other elements within the sort range of the array the sort will work, in which case a VT_EMPTY will be considered less than a VT_NULL and both are ordered before any actual data in the array.

On Indexed Sorts:

In contrast to a standard array sort routine in which the data in the array is re-arranged, Index sorts leave the original array intact and instead sort an array of incremented integer values (1, 2, 3, 4, etc). The sorted index array holds the element numbers of the original array in a sorted order.

For example:

Before the sort:

    Index (1) = 1	 Array(1) = 16

    Index (2) = 2	 Array(2) = 23

    Index (3) = 3	 Array(3) = 7

     After the sort:

     Array() stays the same

     Index(1) = 3	 because Array(3) = 7	i.e. lowest value

     Index(2) = 1	 because Array(1) = 16	i.e. middle value

     Index(3) = 2	 because Array(2) = 23	i.e. highest value

Before you call an indexed sort routine, you must initialize the index array with incrementing integer values. You can use the QuickPak Professional for Windows InitInt routine to do this.

For example, imagine you have an integer array called Array%(1 to 100) which contains random values. The following code will perform an indexed sort and display the values in sorted order, while keeping the original array intact.

     '-- Create an index array

     ReDim IndexArray%(1 to 100)

     '-- Set the values to be 1, 2, 3,...100

     Call InitInt (IndexArray%(1), 1, 100)

     '-- Set up the variables that are passed to ArrayISortNum

     NumEls% = 100

     Direction% = 0

     MemberSize% = -1	 '-- see data type chart above (-1 = Integer)

     '-- Sort the index array based on the contents of Array%()

     Call ArrayISortNum (IndexArray%(), Array%(), Array%(1), NumEls%,

     Direction%, MemberSize%)

        '-- Display the original array elements in sorted order.

     For i% = 1 to 100

        NextElement% = IndexArray%(i%)

        NextValue% = Array%(NextElement%)

        Debug.Print NextValue%

     Next

See Also:

ArrayISortNum, ArrayISortStr, ArrayISortTyp, ArraySortNum,  ArraySortStr, ArraySortTyp, ArraySortVar


ArraySortNum

assembler subroutine contained in QPRO200.DLL

Purpose:

ArraySortNum will sort all or part of a huge (maybe larger than 64K) single or multi-dimensional numeric array, in either ascending or descending order.

Declare Syntax:

Declare Sub ArraySortNum Lib "QPRO200.DLL" (Array() As Any, FirstEl As Any, _

      ByVal NumEls%, ByVal Direction%, ByVal MemberSize%)

Call Syntax:

ArraySortNum Array(), Array(Start), NumEls%, Direction%, MemberSize%

Where:

Array() is the array to sort.

Array(Start) is the first element to include in the sort. **

NumEls% is the number of elements to include in the sort.

Direction% is zero for an ascending sort, or nonzero to sort descending.

MemberSize% determines the type of data being sorted. ***

Comments:

The ArraySort family of routines are replacements for the SortX and ISortX routines. If you are using Visual Basic 2.0 or higher with a huge array and the array element size is either 16384 or not an even power of 2, then we strongly suggest using the ArraySort routines. Otherwise, use the SortX and ISortX routines, as they are generally faster (but are limited to arrays whose element size is not 16384 or an even power of 2).

* Although the Numeric routines indicate  Array() As Any  and FirstEl As Any, these definitions must be one of the five numeric types (i.e. Currency, Double, Integer, Long, and Single.)

** The ArraySort routines will sort a multi-dimensional array by the first dimension. For example, an array might be represented as Array(X,Y), in which case the consecutive elements are Array(X + 1, Y), Array(X + 2, Y), Array(X + 3, Y), etc.

*** Internal comparisons differ not only in the amount of data they must compare, but also in the method of comparison. Acceptable values may be selected from the following set:

    -1	 Integer

    -2	 Long integer

    -3	 Single

    -4	 Double

    -5	 Currency

    -6	 Fuzzy single

    -7	 Fuzzy double

Fuzzy data types are supported for completeness. However, we do not recommend that you use them for sorting. Fuzzy types lack exact precision by nature, and are better suited to searching for a value in an array rather than sorting an array.

See Also:

ArrayISortNum, ArrayISortStr, ArrayISortTyp, ArrayISortVar, ArraySortStr, ArraySortTyp, ArraySortVar


ArraySortStr

assembler subroutine contained in QPRO200.DLL

Purpose:

ArraySortStr will sort all or part of a huge (64K) single or multi-dimensional string array, in either ascending or descending order.

Declare Syntax:

Declare Sub ArraySortStr Lib QPRO200.DLL (Array() As String, FirstEl As String, _

     ByVal NumEls%, ByVal Direction%, ByVal CaseSensitive%)

Call Syntax:

ArraySortStr Array$(), Array$(Start), NumEls%, Direction%, CaseSensitive%

Where:

Array$() is the array to sort.

Array$(Start) is the first element to include in the sort.*

NumEls% is the number of elements to include in the sort.

Direction% is zero for an ascending sort, or nonzero to sort descending.

CaseSensitive% is zero to ignore, or nonzero to honor capitalization.

Comments:

The ArraySort family of routines are replacements for the SortX and ISortX routines. If you are using Visual Basic 2.0 or higher with a huge array and the array element size is either 16384 or not an even power of 2, then we strongly suggest using the ArraySort routines. Otherwise, use the SortX and ISortX routines, as they are generally faster (but are limited to arrays whose element size is not 16384 or an even power of 2).

* The ArraySort routines will sort a multi-dimensional array by the first dimension. For example, an array might be represented as Array(X,Y), in which case the consecutive elements are Array(X + 1, Y), Array(X + 2, Y), Array(X + 3, Y), and so on.

See Also:

ArrayISortNum, ArrayISortStr, ArrayISortTyp, ArrayISortVar, ArraySortNum, ArraySortTyp, ArraySortVar


ArraySortTyp

assembler subroutine contained in QPRO200.DLL

Purpose:

ArraySortTyp will sort all or part of a huge (64K) single or multi-dimensional array of any type, in either ascending or descending order.

Declare Syntax:

Declare Sub ArraySortTyp Lib "QPRO200.DLL" (Array() As Any, FirstEl As Any, _

        ByVal NumEls%, ByVal Direction%, ByVal ElSize%, ByVal MemberOffset%, ByVal_

        MemberSize%, ByVal CaseSensitive%)

Call Syntax:

ArraySortTyp Array(), Array(Start), NumEls%, Direction%, ElSize%, MemberOffset%,_

      MemberSize%, CaseSensitive%

Where:

Array() is the array to sort.

Array(Start) is the first element to include in the sort. *

NumEls% is the number of elements to include in the sort.

Direction% is zero for an ascending sort, or nonzero to sort descending.

ElSize% is the size in bytes of each element in the Type.  It is most easily passed as Len(Array(Start)).

MemberOffset% is the offset into the user defined type of the key member to consider for the sort.  For a fixed length string array use zero.

MemberSize% determines the type of data to consider. **

CaseSensitive% is Zero to ignore, or nonzero to honor capitalization.

Comments:

The ArraySort family of routines are replacements for the SortX and ISortX routines. If you are using Visual Basic 2.0 or higher with a huge array and the array element size is either 16384 or not an even power of 2, then we strongly suggest using the ArraySort routines. Otherwise, use the SortX and ISortX routines, as they are generally faster (but are limited to arrays whose element size is not 16384 or an even power of 2).

* The ArraySort routines will sort a multi-dimensional array by the first dimension. For example, an array might be represented as Array(X,Y), in which case the consecutive elements are Array(X + 1, Y), Array(X + 2, Y), Array(X + 3, Y), and so on.

** Internal comparisons differ not only in the amount of data they must compare, but also in the method of comparison. Acceptable values may be selected from the following set:

        +n	Fixed length string of n bytes

        -1	Integer

        -2	 	Long integer

        -3	Single

        -4	Double

        -5	Currency

        -6	Fuzzy single

        -7	Fuzzy double

        -8	Variant

        -9	String (variable length)

Fuzzy data types are supported for completeness. However, we do not recommend that you use them for sorting because of their nature. If you sort an array with fuzzy logic, some numbers may be out of order due to the nature of a fuzzy search.

On Variant arrays and Variants in Types:

Since the Type sorts will accept the Variant MemberSize, and the Variant Sorts do not require a variant type identifier, it should be noted that the FirstEl argument is used to determine the type of internal compare that is to be used for the sort.  The variant data types are listed here for your reference:

VT_EMPTY

 	VT_NULL

 	VT_I2

 	VT_I4

 	VT_R4

 	VT_R8

 	VT_I8

 	VT_DATE

 	VT_STRING

NOTE: VT_EMPTY and VT_NULL variant data types do not indicate the type of sort desired. If Array(Start) is of either type the sort will not work. When VT_EMPTY and/or VT_NULL appear in other elements within the sort range of the array the sort will work, in which case a VT_EMPTY will be considered less than a VT_NULL and both are ordered before any actual data in the array.

See Also:

ArrayISortNum, ArrayISortStr, ArrayISortTyp, ArrayISortVar, ArraySortNum, ArraySortStr, ArraySortVar


ArraySortVar

assembler subroutine contained in QPRO200.DLL

Purpose:

ArraySortVar will sort all or part of a huge (64K) single or multi-dimensional variant array, in either ascending or descending order.

Declare Syntax:

Declare Sub ArraySortVar Lib "QPRO200.DLL" (Array() As Variant, FirstEl As _

        Variant, ByVal NumEls%, ByVal Direction%, ByVal CaseSensitive%)

Call Syntax:

ArraySortVar Array(), Array(Start), NumEls%, Direction%, CaseSensitive%

Where:

Array() is the array to sort.

Array(Start) is the first element to include in the sort. *

NumEls% is the number of elements to include in the sort.

Direction% is zero for an ascending sort, or nonzero to sort descending.

CaseSensitive% is zero to ignore, or nonzero to honor capitalization.

Comments:

The ArraySort family of routines are replacements for the SortX and ISortX routines. If you are using Visual Basic 2.0 or higher with a huge array and the array element size is either 16384 or not an even power of 2, then we strongly suggest using the ArraySort routines. Otherwise, use the SortX and ISortX routines, as they are generally faster (but are limited to arrays whose element size is not 16384 or an even power of 2).

* The ArraySort routines will sort a multi-dimensional array by the first dimension. For example, an array might be represented as Array(X,Y), in which case the consecutive elements are Array(X + 1, Y), Array(X + 2, Y), Array(X + 3, Y), etc.

On Variant arrays and Variants in Types:

Since the Type sorts will accept the Variant MemberSize and the Variant Sorts do not require a variant type identifier, it should be noted that the FirstEl argument determines the type of comparison that is used for sorting.  The variant data types are listed here for your reference:

VT_EMPTY

 	VT_NULL

 	VT_I2

 	VT_I4

 	VT_R4

 	VT_R8

 	VT_I8

 	VT_DATE

 	VT_STRING

NOTE: VT_EMPTY and VT_NULL variant data types do not indicate the type of sort desired. If Array(Start) is of either type the sort will not work. When VT_EMPTY and/or VT_NULL appear in other elements within the sort range of the array the sort will work, in which case a VT_EMPTY will be considered less than a VT_NULL and both are ordered before any actual data in the array.

See Also:

ArrayISortNum, ArrayISortStr, ArrayISortTyp, ArrayISortVar, ArraySortNum, ArraySortStr, ArraySortTyp


CSFindLast

assembler function contained in QPRO200.DLL

Purpose:

CSFindLast scans a conventional (not fixed-length) string array backwards looking for the last non-blank element.

Declare Syntax:

Declare Function CSFindLast% Lib "QPRO200.DLL" (Array$, ByVal NumEls%)

Call Syntax:

Last% = CSFindLast%(Array$(NumEls%), NumEls%)

Where:

NumEls% is the number of elements to which Array$ has been dimensioned, and Last% receives the number of the last element that is not null.

Comments:

CSFindLast is useful when writing routines that need to know how many elements in a string array are actually active, even if the array might have been dimensioned to a larger value.

Though UBound will report how large the entire array is, in many cases what you really want to know is what portion of the array contains useful information.

Also see:

Find and Find2, FindB and FindB2, FindExact, FindT and FindT2, FindTB and FindTB2, FindV and Find V2, FindVB, FindVB2


DeleteStr

assembler subroutine contained in QPRO200.DLL

Purpose:

DeleteStr removes an element from a normal (not fixed-length) string array.

Declare Syntax:

Declare Sub DeleteStr Lib "QPRO200.DLL" (Array$, ByVal NumEls%)

Call Syntax:

DeleteStr Array$(Element%), NumEls%

Where:

Array$(Element%) is the element to be deleted, and NumEls% is the number of elements that follow.

Comments:

DeleteStr is functionally equivalent to a For/Next loop that copies elements downward, but it operates much faster than would be possible using Visual Basic alone. The routine does not actually create or assign strings, therefore the deletion is handled internally by swapping. The algorithm used is:

    For X = Element% To Element% + NumEls%

       Swap Array$(X), Array$(X + 1), 0

    Next

Because the deletion is performed by swapping, the deleted string ends up in the last element of the array. You can easily erase the last element like this:

    Array$(Element% + NumEls%) = ""

Example Location:

Project File    Form/BASIC File     Procedure

INSDEL.MAK         INSDEL.MAK            Delete1_Click


DeleteT

assembler subroutine contained in QPRO200.DLL

Purpose:

DeleteT removes an element from numeric or user-defined Type array.

Declare Syntax:

Declare Sub DeleteT Lib "QPRO200.DLL" (Array As Any, ByVal ElSize%, ByVal NumEls%)

Call Syntax:

DeleteT Array(Element%), ElSize%, NumEls%

Where:

Array(Element%) is the element to be deleted, ElSize% is the size of each array element (or a special code that is described below), and NumEls% is the number of elements that follow.

Comments:

DeleteT is functionally equivalent to a For/Next loop that copies elements downward, but it operates much faster than would be possible using Visual Basic alone. Unlike the Delete routine meant for string arrays, DeleteT performs a direct memory move copying as many bytes as needed to effect the deletion. Because memory is simply copied downward into lower elements, the last element is still present in the array. You can easily clear it if needed like this:

     Array(Element% + NumEls%) = 0 'numeric element

The element size (ElSize%) indicates how many bytes each element occupies. For example, you could use 2 for an integer array, and 8 for a double precision. DeleteT will also accept the codes used by the Type sort routines to indicate the element size:

 -1 = integer

    	 -2 = long integer

   	 -3 = single precision

  	 -4 = double precision

 	 -5 = currency

 -8 = variant

 +n = Type with a size of n bytes

Example Location:

Project File   	Form/BASIC File 	Procedure

INSDEL.MAK	INSDEL.FRM           	Delete2_Click

Also see:

InsertStr, InsertT, DeleteStr


DimBits

Visual Basic subroutine contained in the file DIMBITS.BAS

Purpose:

DimBits creates a Visual Basic string that will be used to hold a Bit array.

Call Syntax:

DimBits Array$, NumEls%

Where:

Array$ is returned set to the length needed to hold NumEls% bit elements, and it is initialized to all zeros.

Comments:

Bit arrays are useful when a large table of True/False information must be maintained in as little memory as possible. Where a conventional integer array requires two bytes to hold even a single bit of information, a Bit array occupies only the amount of memory actually needed.

Once a Bit array has been dimensioned, individual elements are read and assigned using GetBit and SetBit respectively. These routines are described elsewhere in this manual. Bit arrays are also discussed at length in the section of this manual entitled "Bit Arrays".

DimBits is a very simple routine, and may be entered into your program directly. The actual code is:

    Array$ = String$(NumEls \ 8 + 1, 0)

Example Location:

Project File     	Form/BASIC File      	Procedure

BITARRAY.MAK 	BITARRAY.FRM         	Form_Load

See Also:

GetBit, SetBit


Fill2, 4, 8

assembler subroutines contained in QPRO200.DLL

Purpose:

Fill2 will quickly assign all of the elements in a specified portion of an integer array to any value.

Declare Syntax:

Declare Sub Fill2 Lib "QPRO200.DLL" (Array%, ByVal Value%, ByVal NumEls%)

Declare Sub Fill4 Lib "QPRO200.DLL" (Array As Any, Value As Any, ByVal NumEls%)

Declare Sub Fill8 Lib "QPRO200.DLL" (Array As Any, Value As Any,ByVal NumEls%)

Call Syntax:

Fill2 Array%(Start%), Value%, NumEls%

Fill4 Array&(Start%), Value&, NumEls%

or

Fill4 Array!(Start%), Value!, NumEls%

Fill8 Array#(Start%), Value#, NumEls%

or

Fill8 Array@(Start%), Value@, NumEls%

Where:

Array%(Start%) is the first element to be assigned, Value% is the value the elements are to receive, and NumEls% is the number of elements to be processed.

Comments:

Fill4 and Fill8 are called exactly the same as Fill2, but are set up to process single precision (or long integer) and double precision (or Currency) arrays respectively. All of the passed parameters have the same meaning as with Fill2, though the appropriate Array and Value variables should be substituted.

To initialize any array to all zeros (or null strings), the best approach is to simply re-dimension it:

    ReDim Array(NumEls)

Example Location:

Project File     	Form/BASIC File	Procedure

NARRROPS.MAK   	NARRROPS.FRM	Fill_Clk


Find and Find2

assembler subroutines contained in QPRO200.DLL

Purpose:

Find will search all or part of a conventional (not fixed-length) string array forward looking for the first occurrence of a given string or sub-string.

Declare Syntax:

Declare Sub Find Lib "QPRO200.DLL" (Array$, NumEls%, Search$)

Declare Sub Find2 Lib "QPRO200.DLL" (Array$, NumEls%, Search$)

Call Syntax:

Find Array$(Start%), NumEls%, Search$

Find2 Array$(Start%), NumEls%, Search$

Where:

Array$(Start) is the element which searching is to begin with, NumEls% is the number of successive elements to be examined, and Search$ is the string or sub-string to find.

NumEls% is returned holding the number of elements that were searched until a match was found, or -1 if there were no matching elements. If the first element searched matches, NumEls% will return holding 0.

Comments:

Find is set up to honor capitalization, and Find2 will ignore it. That is, Find2 will find "abcde" within the array element "ABCDE", while Find would not. One or more question marks (?) may also be used in the search string as a wild card to match any character. The DOS "*" wild card is implied, and should not be used.

Because Find2 compares strings regardless of capitalization, the first thing it does is to permanently capitalize the search string. In designing Find2 we would have preferred to not tamper with the search string, however that would have decreased its performance.

If it is essential that Search$ not be modified, simply pass a copy of Search$ as shown below:

Find2 Array$(Start), NumEls%, UCase$(Search$)

When UCase$ (or LCase$) is used within a calling argument, Visual Basic will first copy the string into a temporary location, and then capitalize the copy. Thus, no matter what Find2 does to the search string, only the temporary copy will be affected.

Example Location (Find, Find2):

Project File   	Form/BASIC File     Procedure

ARRFIND.MAK	ARRFIND.FRM         DoSearch


FindB and FindB2

assembler subroutines contained in QPRO200.DLL

Purpose:

FindB will search all or part of a conventional (not fixed-length) string array for a string or sub-string. Like Find, FindB also accepts wild cards and is provided in both case sensitive and insensitive versions. However, FindB and FindB2 search the string array backwards.

Declare Syntax:

Declare Sub FindB Lib "QPRO200.DLL" (Array$, NumEls%, Search$)

Declare Sub FindB2 Lib "QPRO200.DLL" (Array$, NumEls%, Search$)

Call Syntax:

FindB Array$(NumEls%), NumEls%, Search$

FindB2 Array$(NumEls%), NumEls%, Search$

Where:

Array$(NumEls%) is the current array element at which searching is to begin, NumEls% is its element number, and Search$ is the string or sub-string to find.

NumEls% is returned holding the actual number of the element in which the match was found, or -1 if there were no matching elements. However, this assumes that the array begins at element zero.

Comments:

All of the comments for Find and Find2 apply to FindB and FindB2, except that continuing an array search is handled differently.

Example Location (Find, Find2, FindB, FindB2):

Project File	Form/BASIC File	Procedure

ARRFIND.MAK	ARRFIND.FRM	DoSearch

See Also:

Find and Find2, FindExact, CSFindLast, FindT and FindT2, FindTB and FindTB2, FindV and Find V2, FindVB, FindVB2


FindExact

assembler subroutine contained in QPRO200.DLL

Purpose:

FindExact searches an entire conventional (not fixed-length) string array for an exact match.

Declare Syntax:

Declare Sub FindExact Lib "QPRO200.DLL" (Array$, NumEls%, Search$)

Call Syntax:

FindExact Array$(Start%), NumEls%, Search$

Where:

Array$(Start%) is the element to begin searching with, NumEls% is the total number of elements to examine, and Search$ is the string to search for.  NumEls% returns holding the number of elements it searched before finding a match, with 0 meaning it was found on the first one.  If no match is found NumEls% will instead hold -1.

Comments

Unlike Find, FindB, and the other "Find" routines, this one searches for an exact match only.  Since capitalization is honored and wild cards are not recognized, this routine is much faster than the other versions.

Also see:

Find and Find2, FindB and FindB2, CSFindLast, FindT and FindT2, FindTB and FindTB2, FindV and Find V2, FindVB, FindVB2


FindT and FindT2

assembler subroutines contained in QPRO200.DLL

Purpose:

FindT will search all or part of a type array for any string or sub-string.

Declare Syntax:

Declare Sub FindT Lib "QPRO200.DLL" (Array As Any, ByVal ElSize%, NumEls%, Search$)

Declare Sub FindT2 Lib "QPRO200.DLL" (Array As Any, ByVal ElSize%,NumEls%, Search$)

Call Syntax:

FindT Array(Start%), ElSize%, NumEls%, Search$

FindT2 Array(Start%), ElSize%, NumEls%, Search$

Where:

Array(Start%) is the first element to begin searching, ElSize% is the length of each element, NumEls% is the number of successive elements to be examined, and Search$ is the string or sub-string to find.

NumEls% is returned holding the number of elements that were searched until a match was found, or -1 if there were no matching elements. If the first element searched matches, NumEls% will return holding 0.

Comments:

FindT is set up to honor capitalization, and FindT2 will ignore it. That is, FindT2 will find "abcde" within the array element "ABCDE", while FindT will not. One or more question marks (?) may also be used in the search string as a wild card to match any character. The DOS "*" wild card is implied, and should not be used.

Because FindT2 compares strings regardless of capitalization, the first thing it does is to permanently capitalize the search string. In designing FindT2 we would have preferred to not tamper with the search string, however that would have decreased its performance.

If it is essential that Search$ not be modified, simply pass a copy of Search$ as shown below:

   FindT2 Array(Start%), ElSize%, NumEls%, Ucase$(Search$)

When UCase$ (or LCase$) is used within a calling argument, Visual Basic will first copy the string into a temporary location, and then capitalize the copy. Thus, no matter what FindT2 does to the search string, only the temporary copy will be affected.

This routine does not accept variants.

Example Location:

Project File     	Form/BASIC File     Procedure

     DEMOFIND.MAK     	FINDSTR.FRM          FindTPB_Click

         DEMOFIND.MAK     	FINDSTR.FRM          FindT2PB_Click

Also see:

Find and Find2, FindB and FindB2, FindExact, CSFindLast, FindTB and FindTB2 , FindV and Find V2, FindVB, FindVB2


FindTB and FindTB2

assembler subroutines contained in QPRO200.DLL

Purpose:

FindTB will search all or part of a type array for a string or sub-string. Like FindT, FindTB also accepts wild cards and is provided in both case sensitive and insensitive versions. However, FindTB and FindTB2 search the array backwards.

Declare Syntax:

Declare Sub FindTB Lib "QPRO200.DLL" (Array As Any, ByVal ElSize%, CurEl%, Search$)

Declare Sub FindTB2 Lib "QPRO200.DLL" (Array As Any, ByVal ElSize%, CurEl%, Search$)

Call Syntax:

FindTB Array(CurEl%), ElSize%, CurEl%, Search$

FindTB2 Array(CurEl%), ElSize%, CurEl%, Search$

Where:

Array(CurEl%) is the current array element with which to begin searching, CurEl% is its element number, and Search$ is the string or sub-string to find.

CurEl% is returned holding the actual number of the element in which the match was found, or -1 if there were no matching elements. However, this assumes that the array begins at element zero.

Comments:

All of the comments for FindT and FindT2 apply to FindTB and FindTB2, except that continuing an array search is handled differently.

These routines do not accept varients.

Example Location:

       Project File    	Form/BASIC File     	Procedure

                DEMOFIND.MAK    	FINDSTR.FRM         	FindTBPB_Click

                DEMOFIND.MAK    	FINDSTR.FRM        	FindTB2PB_Click

Also see:

Find and Find2, FindB and FindB2, FindExact, CSFindLast, FindT and FindT2, FindV and Find V2, FindVB, FindVB2


FindV and FindV2

assembler functions in QPRO200.DLL

Purpose:

FindV will search all or part of a Variant array forward for the first occurrence of a given Variant.

Declare Syntax:

Declare Function FindV% Lib "QPRO200.DLL" (Array as Variant, NumEls as Variant, _

        Search as Variant)

Declare Function FindV2% Lib "QPRO200.DLL" (Array as Variant, NumEls as Variant, _

        Search as Variant)

Call Syntax:

Failure% = FindV% (Array(Start%), NumEls, Search)

Failure% = FindV2% (Array(Start%), NumEls, Search)

Where:

Failure% is returned as a non-zero value if an error occurred.

Array(Start%) is the element in the Variant array with which searching is to begin.

NumEls is the number of successive elements to be examined. Consecutive elements will be examined until a match is found (See note 1).

NumEls is returned holding the number of elements that were searched until a match was found, or -1 if there were no matching elements. If the first element matches, NumEls will return a value of 0.

Search is the data to look for; it also controls the mode of the search  (See note 2).

Comments:

For string searches:

FindV will honor capitalization, while FindV2 will ignore it.

One or more question marks (?) may also be used in the Search string as a wildcard to match any character.  Further, the DOS "*" (wild card) is implied as the leading and trailing criteria in Search.

For all other types of the Variant Search, FindV2 is identical to FindV.

NOTE 1:

When working with a Multi-dimensioned array, the notion of consecutive elements becomes more of a challenge. The order of storage in memory is the controlling factor. Keep in mind that for Array(I, J, K, ... Z), consecutive elements will be stored at Array(I+1, J, K, ... Z) and Array(I+2, J, K, ... Z) etc. Therefore, Array(I, J, K, ... Z) and Array(I, J, K+1, ... Z) will be separated in memory by (IMax - IMin + 1) * (JMax - JMin + 1) elements (IMin & IMax are LBOUND & UBOUND of the indices), and (16 * elements) bytes.

NOTE 2:

The mode of the search is defined as the type of comparisons to be performed on the individual data items, while looking for a match.

Comparisons will occur as though each element were of the same variable type as Search.

Points to consider:

        If Search is of the variant type EMPTY, the first EMPTY element will be found.

        If Search is of the variant type NULL, the first NULL element will be found.

        If Search is of the variant type 2-7 including DATE, numeric equivalency will be tested.

        If Search is of the variant type STRING, (Len = 0, "" i.e. null), neither EMPTY nor NULL elements will

        be considered a match.

Also see:

Find and Find2, FindB and FindB2, FindExact, CSFindLast, FindT and FindT2, FindTB and FindTB2, FindVB, FindVB2


FindVB, FindVB2

assembler functions in QPRO200.DLL

Purpose:

FindVB will search all or part of a Variant array, for the first occurrence of a given Variant. Like FindV, FindVB also accepts wild cards and is provided in both case sensitive and insensitive versions. However, FindVB and FindVB2 search the array backwards.

Declare Syntax:

Declare Function FindVB% Lib "QPRO200.DLL" (Array as Variant, NumEls as Variant, _

         Search as Variant)

Declare Function FindVB2% Lib "QPRO200.DLL" (Array as Variant, NumEls as Variant, _

         Search as Variant)

Call Syntax:

Failure% = FindVB% (Array(NumEls%), NumEls, Search)

Failure% = FindVB2% (Array(NumEls%), NumEls, Search)

Where:

Failure% is returned as a non-zero value if an error occurred.

Array(NumEls%) is the current array element with which to begin searching, and NumEls% is its element number.  Consecutive Elements will be examined until a match is found. See note 1.

NumEls% is returned holding the actual number of the element in which the match was found, or -1 if there were no matching elements.

Search is the data to look for; it also controls the mode of the search.  See note 2.

Comments:

For string searches:

 FindV will honor capitalization, while FindV2 will ignore it search.

      One or more question marks (?) may also be used in the Search string as a wildcard to match any       character.

      Further, the DOS "*" (wild card) is implied as the leading and trailing criteria in Search.

      For all other types of the Variant Search, FindVB2 is identical to FindVB.

NOTE 1:

When working with a Multi-dimensioned array the notion of consecutive elements becomes more of a challenge. The order of storage in memory is the controlling factor. Keep in mind that for Array(I, J, K, ... Z), consecutive elements will be stored at Array(I+1, J, K, ... Z) and Array(I+2, J, K, ... Z) etc. Therefore, Array(I, J, K, ... Z) and Array(I, J, K+1, ... Z) will be separated in memory by (IMax - IMin + 1) * (JMax - JMin + 1) elements (IMin & IMax are LBOUND & UBOUND of the indices), and (16 * elements) bytes.

NOTE 2:

The mode of the search is defined as the type of comparisons to perform on the individual data items while looking for a match.

Comparisons will occur as though each element were of the same variable type as Search.

Points to consider:

If Search is of the variant type EMPTY, the first EMPTY element will be found.

If Search is of the variant type NULL, the first NULL element will be found.

If Search is of the variant type 2-7 including DATE, numeric equivalency will be tested.

If Search is of the variant type STRING, (Len = 0, "" i.e. null), neither EMPTY nor NULL elements will be considered a match.

Also see:

Find and Find2, FindB and FindB2, FindExact, CSFindLast, FindT and FindT2, FindTB and FindTB2, FindV and Find V2


GetBit

assembler function contained in QPRO200.DLL

Purpose:

GetBit will report the status (on or off) of an element in a QuickPak Professional for Windows Bit array.

Declare Syntax:

Declare Function GetBit% Lib "QPRO200.DLL" (Array$, ByVal Element%)

Call Syntax:

Bit% = GetBit%(Array$, Element%)

Where:

Array$ is a string that was previously set up to hold a Bit array, Element% is the desired element number to retrieve, and Bit receives either a 0 if the element is clear, or -1 if it is set.

Comments:

GetBit is set up to return -1 if the element is set rather than a normal 1. This allows the use of the Visual Basic Not operator, and eliminates the requirement for an explicit comparison against a particular value. For example, to see if a bit is set could be done like this:

     If GetBit%(Array$, 1499) Then . . .

And to determine if the bit is clear, you could instead use:

     If Not GetBit%(Array$, 1499) Then . . .

Using this approach also eliminates having to perform an explicit comparison to obtain the bit status:

     If GetBit%(Array$, 20998) = 0 Then . . .

Bit arrays are discussed in depth separately in this manual under the heading "Bit Arrays".

Example Location:

     Project File    	Form/BASIC File    	Procedure

             BITARRAY.MAK 	BITARRAY.FRM 	Array_Lostfocus

Also see:

DimBits, SetBit


IMaxC, D, I, L, S

assembler functions contained in QPRO200.DLL

Purpose:

IMaxC will search through an entire currency array, and return the element number of the largest value. The remaining functions are designed to operate on double precision, integer, long integer, and single precision arrays respectively.

Declare Syntax:

Declare Function IMaxC% Lib "QPRO200.DLL" (Array@, ByVal NumEls%)

Declare Function IMaxD% Lib "QPRO200.DLL" (Array#, ByVal NumEls%)

Declare Function IMaxI% Lib "QPRO200.DLL" (Array%, ByVal NumEls%)

Declare Function IMaxL% Lib "QPRO200.DLL" (Array&, ByVal NumEls%)

Declare Function IMaxS% Lib "QPRO200.DLL" (Array!, ByVal NumEls%)

Call Syntax:

Element% = IMaxC%(Array@(Start%), NumEls%)

Element% = IMaxD%(Array#(Start%), NumEls%)

Element% = IMaxI%(Array%(Start%), NumEls%)

Element% = IMaxL%(Array&(Start%), NumEls%)

Element% = IMaxS%(Array!(Start%), NumEls%)

Where:

Array(Start%) is the first element to consider when searching, NumEls% is the total number of elements to search, and Element receives the element number that holds the largest value.

Comments:

Unlike the Max? functions that directly return the value of the largest element, these functions return an element number and are thus designed as integer functions. If you need both the element number and the value, using only the IMax function will give you both and avoid adding redundant code to your program.

The element number returned assumes a zero-based array, and further, that the entire array is being searched. That is, if the largest value is found at the first element, the function will return zero. To accommodate varying start elements simply add the first element number to the answer returned:

   Element = IMaxD%(Array#(10), NumEls%) 	  'start at element 10

   Element = Element + 10 	  'so add 10 here

Example Location:

      Project File   Form/BASIC File    Procedure

      NARRRPT.MAK    NARRRPTS.FRM       GotoMax_Click

Also see:

IMinC, D, I, L, S


IMinC, D, I, L, S

assembler functions contained in QPRO200.DLL

Purpose:

IMinC will search through an entire currency array, and return the element number of the smallest value. The remaining functions are designed to operate on double precision, integer, long integer, and single precision arrays respectively.

Declare Syntax:

Declare Function IMinC% Lib "QPRO200.DLL" (Array@, ByVal NumEls%)

Declare Function IMinD% Lib "QPRO200.DLL" (Array#, ByVal NumEls%)

Declare Function IMinI% Lib "QPRO200.DLL" (Array%, ByVal NumEls%)

Declare Function IMinL% Lib "QPRO200.DLL" (Array&, ByVal NumEls%)

Declare Function IMinS% Lib "QPRO200.DLL" (Array!, ByVal NumEls%)

Call Syntax:

Element% = IMinC%(Array@(Start%), NumEls%)

Element% = IMinD%(Array#(Start%), NumEls%)

Element% = IMinI%(Array%(Start%), NumEls%)

Element% = IMinL%(Array&(Start%), NumEls%)

Element% = IMinS%(Array!(Start%), NumEls%)

Where:

Array(Start%) is the first element to consider when searching the array, NumEls% is the total number of elements to search, and Element receives the element number that holds the lowest value.

Comments:

Unlike the Min? functions that directly return the value of the smallest element, these functions return an element number or Index and are thus designed as integer functions. If you need both the element number and the value, using only the IMin function will give you both and avoid adding redundant code to your program.

The element number returned assumes a zero-based array, and further, that the entire array is being searched. That is, if the smallest value is found at the first element, the function will return zero. To accommodate varying start elements simply add the first element number to the answer returned:

   Element = IMinD%(Array#(10), NumEls%)	 	'start at element 10

   Element = Element + 10	 	'therefore add 10 here

Example Location:

     Project File    Form/BASIC File     Procedure

     NARRRPT.MAK     NARRRPT.FRM         GotoMin_Click

Also see:

IMaxC, D, I, L, S


InitInt

assembler subroutine contained in QPRO200.DLL

Purpose:

InitInt will quickly initialize all or a specified portion of an integer array with increasing values.

Declare Syntax:

Declare Sub InitInt Lib "QPRO200.DLL" (Array%, ByVal StartValue%, ByVal NumEls%)

Call Syntax:

InitInt Array%(Start%), StartValue%, NumEls%

Where:

Array%(Start%) is assigned StartValue%, Array%(Start + 1) is assigned Value% + 1, and so forth, and NumEls% is the total number of elements to assign.

Comments:

InitInt is required to initialize the integer index array that will be used with any of the indexed sorting routines. Though you could certainly initialize an array with a For/Next loop, this dedicated routine will do it much more quickly.

For more information regarding InitInt and the QuickPak Professional for Windows indexed sort routines, see the section entitled "Sorts vs. Indexed Sorts" elsewhere in this manual.

Example Location:

     Project File    Form/BASIC File   Procedure

     STRSORT.MAK     STRSORT.FRM       Create_Click


InsertStr

assembler subroutine contained in QPRO200.DLL

Purpose:

InsertStr will insert an element at any point into a conventional (not fixed-length) string array.

Declare Syntax:

Declare Sub InsertStr Lib "QPRO200.DLL" (Array$, Ins$, ByVal NumEls%)

Call Syntax:

InsertStr Array$(Element%), Ins$, NumEls%

Where:

Ins$ will be inserted into the array at the element specified by Element%, and NumEls% is the total number of elements that follow.

Comments:

InsertStr is functionally equivalent to a For/Next loop that copies elements upward, but it operates much faster than would be possible using Visual Basic alone. An assembler routine cannot actually create or assign strings, therefore the insertion is handled internally by swapping. The algorithm used is:

    For X = (Element% + NumEls%) To (Element% + 1) Step -1

       Swap Array$(X), Array$(X - 1), 0

    Next

    Swap Array$(Start), Ins$, 0

Because the insertion is performed by swapping, the string that had originally been in Array$(Element%) ends up in Ins$ when the routine has finished.

It is up to you to insure that the array has been sufficiently dimensioned to accommodate the inserted element. Specifying more elements than actually exist is certain to cause a crash. However, the elements that follow Array$(Element%) may be null.

Example Location:

      Project File    Form/BASIC File    Procedure

      INSDEL.MAK      INSDEL.FRM         Insert1_Click

Also see:

InsertT


InsertT

assembler subroutine contained in QPRO200.DLL

Purpose:

InsertT inserts an element into a numeric or user-defined Type array.

Declare Syntax:

Declare Sub InsertT Lib "QPRO200.DLL" (Array As Any, ByVal ElSize%, ByVal NumEls%)

Call Syntax:

InsertT Array(Element%), ElSize%, NumEls%

Where:

Array(Element%) is the point at which the insertion is to take place, ElSize% is the size of each array element in bytes (or a special code that is described below), and NumEls% is the number of elements that follow.

Comments:

InsertT is functionally equivalent to a For/Next loop that copies elements upward, but it operates much faster than would be possible using Visual Basic alone. Unlike the Insert routine meant for string arrays, InsertT performs a direct memory move copying as many bytes as needed to effect the insertion.

Because memory is simply copied upward into higher elements, the current element is still present in the array. Also, unlike the Insert routine for conventional string arrays, you do not specify an element to be inserted. However inserting the element would then be as simple as this:

    Array(Element%) = NewElement

The element size indicates how many bytes each element occupies. For example, you would use 2 for an integer array, and 4 for a long integer. InsertT will also accept the same code as the Type sort routines to indicate the element size.

   -1 = integer

   -2 = long integer

   -3 = single precision

   -4 = double precision

   -5 = currency

   -8 = variant

   +n = Type with a size of n bytes

Example Location:

     Project File   Form/BASIC File    Procedure

     INSDEL.MAK     INSDEL.FRM         Insert2_Click

Also see:

InsertStr, DeleteT, DeleteStr


ISortC, D, I, L, S

assembler subroutines contained in QPRO200.DLL

Purpose:

The five ISort routines are assembler Quick Sorts for ordering all or a portion of a currency, double precision, integer, long integer, and single precision numeric array. Each is intended to sort a parallel INDEX array into either ascending or descending order.

Declare Syntax:

Declare Sub ISortC Lib "QPRO200.DLL" (Array@, Index%, ByVal NumEls%, ByVal Direction%)

Declare Sub ISortD Lib "QPRO200.DLL" (Array#, Index%, ByVal NumEls%, ByVal Direction%)

Declare Sub ISortI Lib "QPRO200.DLL" (Array%, Index%, ByVal NumEls%, ByVal Direction%)

Declare Sub ISortL Lib "QPRO200.DLL" (Array&, Index%, ByVal NumEls%, ByVal Direction%)

Declare Sub ISortS Lib "QPRO200.DLL" (Array!, Index%, ByVal NumEls%, ByVal Direction%)

Call Syntax:

ISortC Array@(Start%), Index%(0), NumEls%, Direction%

ISortD Array#(Start%), Index%(0), NumEls%, Direction%

ISortI Array%(Start%), Index%(0), NumEls%, Direction%

ISortL Array&(Start%), Index%(0), NumEls%, Direction%

ISortS Array!(Start%), Index%(0), NumEls%, Direction%

Where:

ISort? is the appropriate indexed sort routine.

Array(Start%) is the first element to be included in the sort, and Index%(0) is the starting element in the integer index array. NumEls% is the number of elements to consider, and Direction% indicates the sort direction. If Direction% is set to zero, then the sorting will be forward (ascending). Any other value will cause sorting to be performed backward (descending).

Comments:

Five separate routines are provided to allow you to add only the sort capabilities you need to your program. However, the indexed Type sort routine (ISortT) is capable of sorting all of the variable types, and you might consider using that if your program will need to sort more than one type of variable.

It is extremely important that the integer index array be dimensioned to at least the same size as the portion of the primary array being sorted. It must also be initialized to increasing values prior to calling the indexed sort routines. That is, Index%(0) must contain a zero, Index%(1) will hold a one, and so forth. The routine InitInt is specifically intended for this purpose.

A complete description of these sorts, along with a comparison of indexed vs. normal sorting, is given elsewhere in this documentation. Please see the section "Sorts vs. Indexed Sorts".

Example Location (ISortC, ISortD, ISortI, ISortL, ISortS):

Project File     	Form/BASIC File 	Procedure

INSORTNUM.MAK    	INSORTNUM.FRM     	Sort_Click

Also see:

ISortStr and ISortStr2, ISortT and ISortT2, ISortV and ISortV2


ISortStr and ISortStr2

assembler subroutines contained in QPRO200.DLL

Purpose:

ISortStr will sort all or part of a conventional (not fixed-length) string array into either ascending or descending order by manipulating elements in a parallel integer index array. ISortStr2 is nearly identical, but sorting is performed without regard to capitalization.

Declare Syntax:

Declare Sub ISortStr Lib "QPRO200.DLL" (Array$, Index%, ByVal _

        NumEls%, ByVal Direction%)

Declare Sub ISortStr2 Lib "QPRO200.DLL" (Array$, Index%, ByVal_

        NumEls%, ByVal Direction%)

Call Syntax:

ISortStr Array$(Start%), Index%(0), NumEls%, Direction%

ISortStr2 Array$(Start%), Index%(0), NumEls%, Direction%

Where:

Array$(Start%) is the first element to be included in the sort, and Index%(0) is the starting element in the integer index array. NumEls% is the number of elements to consider, and Direction% indicates the sort direction. If Direction% is set to zero, then the sorting will be forward (ascending). Any other value will cause sorting to be performed backward (descending).

Comments:

It is extremely important that the integer index array be dimensioned to at least the same size as the primary array being sorted. It must also be initialized to increasing values prior to calling the indexed sort routines. That is, Index%(0) must contain a zero, Index%(1) will hold a one, and so forth. The routine InitInt is specifically intended for this purpose.

Also, a complete description of these sorts, along with a comparison of indexed vs. normal sorting, is given elsewhere in this manual. Please see the section "Sorts vs. Indexed Sorts".

Example Location:

     Project File    Form/BASIC File    Procedure

     STRSORT.MAK, STRSORT.FRM, Sort_Click

Also see:

ISortC, D, I, L, S, ISortT and ISortT2, ISortV and ISortV2


ISortT and ISortT2

assembler subroutines contained in QPRO200.DLL

Purpose:

ISortT will sort all or part of a Type array into either ascending or descending order by manipulating elements in a parallel integer index array. ISortT2 is nearly identical, but when considering the string component of a Type array, sorting is performed without regard to capitalization.

Declare Syntax:

Declare Sub ISortT Lib "QPRO200.DLL" (Array As Any, Index%, ByVal NumEls%, ByVal_

        Direction%, ByVal ElSize%, ByVal NemberOffset%, ByVal MemberSize%)

Declare Sub ISortT2 Lib "QPRO200.DLL" (Array As Any, Index%, ByVal NumEls%, ByVal_

        Direction%, ByVal ElSize%, ByVal MemberOffset%, ByVal MemberSize%)

Call Syntax:

ISortT Array(Start%), Index%(0), NumEls%, Direction%, ElSize%, _

        MemberOffset%, MemberSize%

ISortT2 Array(Start%), Index%(0), NumEls%, Direction%, ElSize%,_

        MemberOffset%, MemberSize%

Where:

Array(Start%) is the first element to be included in the sort, and Index%(0) is the starting element in the integer index array. NumEls% is the number of elements to consider, and Direction% indicates the sort direction. If Direction% is set to zero, then the sorting will be forward (ascending). Any other value will cause sorting to be performed backward (descending).

ElSize% is the length of each Type array element, MemberOffset% is the number of bytes into the Type where the key member being considered for the sort is located, and MemberSize% is its length. MemberSize% is coded using the convention described in the Comments section below.

Comments:

A special code is used to indicate the type of variable that is being operated upon. For example, sorting based on the string portion of a Type simply involves comparing the ASCII values for each character. However, sorting on a double precision component of a Type array requires a very different approach.

The type of variable being considered as the key is indicated to ISortT and ISortT2 as follows:

    	O = ByVal conventional string ($)

   	-1 = integer

  	-2 = long integer

  	-3 = single precision

  	-4 = double precision

  	-5 = currency

  	-6 = fuzzy single

  	-7 = fuzzy double

 	-8 = variant

+n = fixed-length string with a length of n bytes

Comments in the description of the ISort routines for numeric arrays contain important information about preparing the integer index array.

Example Location:

   Project File    	Form/BASIC File	Procedure

   TYPESORT.MAK 	TYPESORT.FRM	Sort_Click

Also see:

ISortC, D, I, L, S, 	ISortStr and ISortStr2, ISortV and ISortV2


ISortV and ISortV2

assembler functions in QPRO200.DLL

Purpose:

ISortV will sort all or part of a Variant array into either ascending or descending order, by manipulating elements in a parallel integer index array. ISortV2 is nearly identical, but when considering strings, sorting is performed without regard to capitalization.

Declare Syntax:

Declare Function ISortV% Lib "QPRO200.DLL" (Array as Variant, Index%, _

       ByVal NumEls%, ByVal Direction%)

Declare Function ISortV2% Lib "QPRO200.DLL" (Array as Variant, Index%, _

       ByVal NumEls%, ByVal Direction%)

Call Syntax:

Failure% = SortV% (Array(Start%), Index%(0) ,NumEls%, Direction%)

Failure% = SortV2% (Array(Start%), Index%(0) ,NumEls%, Direction%)

Where:

Failure% is returned as a non-zero value if an error occurred.

Array(Start%) is the first element to consider in the sort; it also determines the mode of the sort (See note 2).

Index%(0) is the starting element of the integer index array and must contain a value of zero. The remaining elements in the Index%() array must be initialized to increasing integer values before sorting.

NumEls% is the total number of elements to consider, and Direction indicates the sort direction. If Direction is set to zero, then sorting will be forward (ascending). Any other value will cause sorting to be performed backward (descending).

Comments:

ISortV will honor capitalization, while ISortV2 will ignore it.

Attempting to sort on variants of type VT_EMPTY & VT_NULL will cause the routine to return True (-1) indicating an error.

If Empties or Nulls occur in the array they will be sorted in the following order:  VT_EMPTY, VT_NULL, variant types 2-8.

For a backwards sort, variant types 2-8, the order will be:  VT_NULL, VT_EMPTY.

NOTE 1:

When working with a Multi-dimensioned array the notion of consecutive elements becomes more of a challenge. The order of storage in memory is the controlling factor. Keep in mind that for Array(I, J, K, ... Z)  consecutive elements will be stored at Array(I+1, J, K, ... Z) and Array(I+2, J, K, ... Z) etc. Therefore, Array(I, J, K, ... Z) and Array(I, J, K+1, ... Z) will be separated in memory by (IMax - IMin + 1) * (JMax - JMin + 1) elements (IMin & IMax are LBOUND & UBOUND of the indices), and (16 * elements) bytes.

NOTE 2:

The mode of the sort is defined as the type of comparisons to be performed on the individual data items.  Comparisons will occur as though each element were of the same variable type as Array(Start%).

A description of these sorts, along with a comparison of indexed vs. normal sorting is given elsewhere in the manual.  Please see the section "Sort vs. Indexed Sorts".

Also see:

ISortC, D, I, L, S, ISortStr and ISortStr2, ISortT and ISortT2,


KeySort

assembler subroutine contained in QPRO200.DLL

Purpose:

KeySort is a recursive subprogram that will sort a user-defined Type array based on any number of keys. Each key may be sorted independently, ascending or descending.

Declare Syntax:

Declare Sub KeySort Lib "QPRO200.DLL" (Array As Any, ByVal ElSize%, ByVal NumEls%, _

  Table%, ByVal NumKeys%)

Call Syntax:

KeySort Array(Start%), ElSize%, NumEls%, Table%(1, 1), NumKeys%

Where:

Array(Start%) is the first element to consider in the Type array, ElSize% is the length in bytes of each element, and NumEls% is the total number of elements to be included. Table%(   ) is a 2-dimensional array which indicates how the primary array is to be sorted (see below), and NumKeys% is the number of sort keys.

Comments:

In order for KeySort to operate correctly, the Table%(  ) array must be properly prepared. The size of the first subscript should be dimensioned to the total number of sort keys, and the second subscript must be dimensioned to 3. For example, to sort a Type array on two keys you would dimension Table%(   ) like this:

Dim Table%(1 To 2, 1 To 3))

In the second subscript, the first element, Table%(n, 1), tells how many bytes into the structure the current key is located. The second element, Table%(n,2), holds the size in bytes of the data, using the codes shown below. The third element (n, 3) is either zero to sort ascending, or anything else to sort descending.

It is important that the table array be dimensioned to the exact size needed. If the main array is to be sorted on, say, 4 keys, then Table%(  ) must be dimensioned to only that size:

Dim Table%(1 To 4, 1 To 3)).

Because KeySort calls upon SortT to do much of the real work, the data type codes are the same as those used by SortT:

 	0 = ByVal conventional string ($)

-1 = 2-byte integer

-2 = 4-byte long integer

-3 = 4-byte single precision

-4 = 8-byte double precision

-5 = currency

-6 = fuzzy single

-7 = fuzzy double

-8 = variant

+n = n-byte fixed-length string

Type Test

 	    I As Integer

    L As Long

            	    S As Single

            	    D As Double

            	    X As String * 20

End Type

NumberOfKeys = 3  'the total number of sort keys

        	Dim Table(1 To NumberOfKeys, 1 To 3)

'Table is a 2-dimensional table of sort information

'  constructed as follows:

'Element 1,1 is the offset into the element for the primary key

'Element 1,2 is a code for the type of data being considered

'Element 1,3 is 0 or 1 for ascending or descending respectively

        	'Element 2,1 is the offset into the element for the secondary

        	'    key, and so forth, through the last sort key

'Primary key (String portion)

Table%(1, 1) = 18 	 'String part starts 18 bytes into structure

Table%(1, 2) = 20 	 'Its length is twenty characters

Table%(1, 3) = 0 	 'Indicate an ascending sort order

'Secondary key (Integer portion)

Table%(2, 1) = 0 	 'The integer starts at an offset of 0

Table%(2, 2) = -1 	 'Its type code is -1 for integers

Table%(2, 3) = 1 	 'Indicate a descending sort order

'Third key (Double precision portion)

Table%(3, 1) = 10 	 'The double precision part's offset is 10

Table%(3, 2) = -4	  'Its type code is -4 for double precision

Table%(3, 3) = 0 	  'Indicate an ascending sort order

Example Location:

Project File	Form/BASIC File	Procedure

KEYSORT.MAK	KEYSORT.FRM  	Sort_Click

Also see:

ISortC, D, I, L, S, ISortStr and ISortStr2, ISortT and ISortT2, ISortV and ISortV2


MaxC, D, I, L, S

five assembler functions contained in QPRO200.DLL

Purpose:

MaxC, MaxD, MaxI, MaxL, and MaxS will return the largest value in a specified portion of a numeric array. Five separate functions are provided, with one each intended for currency, double precision, integer, long integer, and single precision arrays.

Declare Syntax:

Declare Function MaxC@ Lib "QPRO200.DLL" (Array@, ByVal NumEls%)

Declare Function MaxD# Lib "QPRO200.DLL" (Array#, ByVal NumEls%)

Declare Function MaxI% Lib "QPRO200.DLL" (Array%, ByVal NumEls%)

Declare Function MaxL& Lib "QPRO200.DLL" (Array&, ByVal NumEls%)

Declare Function MaxS! Lib "QPRO200.DLL" (Array!, ByVal NumEls%)

Call Syntax:

Max@ = MaxC@(Array@(Start%), NumEls%)

Max# = MaxD#(Array#(Start%), NumEls%)

Max% = MaxI%(Array%(Start%), NumEls%)

Max& = MaxL&(Array&(Start%), NumEls%)

Max! = MaxS!(Array!(Start%), NumEls%)

Where:

Array(Start%) is the first element in the array to be considered, NumEls% is the number of elements to examine, and Max receives the value of the largest element. Of course, both the Max variable and the numeric array must be the appropriate type, based on which Max routine is being used.

Comments:

Also see the companion routines MinC, MinD, MinI, MinL, and MinS .

Example Location (MaxD):

Project File 	Form/BASIC File	Procedure

COMPINT.MAK	FNSPREAD.BAS	QPMAX


MinC, D, I, L, S

five assembler functions contained in QPRO200.DLL

Purpose:

MinC, MinD, MinI, MinL, and MinS will return the smallest value in a specified portion of a numeric array. Five separate functions are provided, with one each intended for currency, double precision, integer, long integer, and single precision arrays.

Declare Syntax:

Declare Function MinC@ Lib "QPRO200.DLL" (Array@, ByVal NumEls%)

Declare Function MinD# Lib "QPRO200.DLL" (Array#, ByVal NumEls%)

Declare Function MinI% Lib "QPRO200.DLL" (Array%, ByVal NumEls%)

Declare Function MinL& Lib "QPRO200.DLL" (Array&, ByVal NumEls%)

Declare Function MinS! Lib "QPRO200.DLL" (Array!, ByVal NumEls%)

Call Syntax:

Min@ = MinC@ (Array@(Start%), NumEls%)

Min# = MinD# (Array#(Start%), NumEls%)

Min% = MinI% (Array%(Start%), NumEls%)

Min& = MinL& (Array&(Start%), NumEls%)

Min! = MinS! (Array!(Start%), NumEls%)

Where:

Array(Start%) is the first element in the array to be considered, NumEls% is the number of elements to examine, and Min receives the value of the smallest element. Of course, both the Min variable and the numeric array must be the appropriate type, based on which Min routine is being used.

Comments:

Also see the companion routines MaxC, MaxD, MaxI, MaxL, and MaxS.

Example Location (MinD):

Project File	Form/BASIC File	Procedure

COMPINT.MAK	FNSPREAD.BAS	QPMIN


Search

assembler subroutine contained in QPRO200.DLL

Purpose:

Search will scan all or part of a numeric array to find the first element that matches a given value. Searching may be performed either forward or backward through the array, and the match may be specified to be exact, less or equal, or greater or equal.

Declare Syntax:

Declare Sub Search Lib "QPRO200.DLL" (Array As Any, ByVal NumEls%,_

        Match As Any, Found%, ByVal Direction%, ByVal MatchCode%, ByVal VarType%)

Call Syntax:

Search Array(Start%), NumEls%, Match, Found%, Direction%, MatchCode%, VarType%

Where:

Array(Start%) is the first array element to include in the search, and may be of any variable type. NumEls% is the total number of elements to search, Match is the value to compare against, and it too may be any type of variable.

Found% then returns a relative displacement (in elements) to the first element that matches. Direction% is set to 0 to search forward, or anything else to search backward.

MatchCode% indicates the type of search as follows:

  0 = find an exact match only (including fuzzy match)

    	  1 = match if the element is greater or equal

   	-1 = match if the element is less or equal

VarType% is coded following the same convention used by the Type sort routines as follows:

   	-1 = 2-byte integer

   	-2 = 4-byte long integer

   	-3 = 4-byte single precision

   	-4 = 8-byte double precision

   	-5 = currency

   	-6 = fuzzy single

   	-7 = fuzzy double

   	-8 = variant

Comments:

Since Real numbers cannot express all rational values exactly, it is easy to see that "1/3" = "0.333333333333..." is a false statement.  Therefore, using Search to find "1.0" in an array made up of values like:

    	N(1) = 0.0

    	N(2) = 0.3333333

    	N(3) = 0.6666666

    	N(4) = 0.9999999

    	N(5) = 1.3333332

    	N(6) =   .

      	.      .

     	.      .

will surely fail to find an exact match using the Search routines data type code of -3 or -4.  The new "fuzzy" data types -5 and -6 have been added to these routines.  The original codes -3 and -4 will still search to the accuracy limit of the Single or Double but the new codes preform a precision clipped comparison.  Hence they will find 1.0 in the N(4) position.

Although the array and the match variables may be any numeric type, they both must, of course, be of the same type.

Example Location:

Project File	Form/BASIC File	Procedure

NARRRPT.MAK	NARRRPT.FRM  	FindIt

Also see:

SearchT and SearchT2


SearchT and SearchT2

assembler functions contained in QPRO200.DLL

Purpose:

SearchT and SearchT2 will search an entire Type array for a match on any single Type member.  When used with the string portion of a Type, SearchT honors capitalization, while SearchT2 ignores it.

Declare Syntax:

Declare Function SearchT% Lib "QPRO200.DLL" (Array As Any, ByVal NumEls%, Match As_

        Any, ByVal Direction%, ByVal Code%, ByVal StructSize%, ByVal MemberOff%, ByVal_

        MemberSize%)

Declare Function SearchT2% Lib "QPRO200.DLL" (Array As Any, ByVal NumEls%, Match As_

        Any, ByVal Direction%, ByVal Code%, ByVal StructSize%, ByVal MemberOff%, ByVal_

        MemberSize%)

Call Syntax:

Found% = SearchT% (Array(Start%), NumEls%, Match, Direction%, Code%, StructSize%,_

        MemberOff%, MemberSize%)

Found% = SearchT2% (Array(Start%), NumEls%, Match,Direction%,Code%, StructSize%,_

        MemberOff%, MemberSize%)

Where:

Array (Start%) is the first Type array element to include in the search.

NumEls% is the number of elements to search.

Match is the value to compare against, and it must be of the same data type as the Type member being

examined.

Any kind of numeric variable MUST use the negative codes!

If Match is a string:

    (1)      It may be may be passed. ByVal and MemberSize% must = 0.  MemberSize% will be calculated as

              Len(Match$). (Use Caution! _ Chr$ (0) terminates the string.)

    (2)     It may be passed directly.  MemberSize% will be calculated as the shorter of Len(Match$), and the value

              given for MemberSize%. If Match$ is shorter than MemberSize%, it has implied trailing wildcard

              characters.

Direction% is 0 for searching forward, or anything else for backward.

Code% indicates the type of search, using the following code:

      0 = exact match (including fuzzy match)

      1 = greater or equal

     -1 = less or equal

StructSize% is the total size of the Type structure in bytes.

MemberOff% is the offset into the structure for the member being sought.

MemberSize% is coded as follows:

    0 = ByVal conventional string ($)

   -1 = 2-byte integer

   -2 = 4-byte long integer

   -3 = 4-byte single precision

   -4 = 8-byte double precision

   -5 = 8-byte currency type

   -6 = fuzzy single

   -7 = fuzzy double

   -8 = variant

   +n = the length of the string portion being searched

Found% is the number of elements searched before the match was found.

Comments:

Since Real numbers cannot express all rational values exactly, it is easy to see that "1/3" = "0.333333333333..." is a false statement.  Therefore, using Search to find 1.0" in an array made up of values like:

   	N(1) = 0.0

  	N(2) = 0.3333333

   	N(3) = 0.6666666

   	N(4) = 0.9999999

   	N(5) = 1.3333332

   	N(6) =   .

  	   .      .

 	   .      .

will surely fail to find an exact match using the Search routines data type code of -3 or -4.  The new "fuzzy" data types -5 and -6 have been added to these routines.  The original codes -3 and -4 will still search to the accuracy limit of the Single or Double but the new codes preform a precision clipped comparison.  Hence they will find 1.0 in the N(4) position.

SearchT and SearchT2 are modeled after the Search routine.  Where Search is limited to a conventional numeric array, SearchT lets you examine just a specified portion of a Type array.

Note that the Code% parameter is ignored when searching for strings, and defaults to 0 (find an exact match).

Example Location:

Project File    	Form/BASIC File	Procedure

NARRRPT.MAK     	NARRRPT.FRM	FindIt

Also see:

Search


SetBit

assembler subroutine contained in QPRO200.DLL

Purpose:

SetBit will set an element in a QuickPak Professional for Windows Bit array to either one or zero (true or false).

Declare Syntax:

Declare Sub SetBit Lib "QPRO200.DLL" (Array$, ByVal Element%, ByVal Bit%)

Call Syntax:

SetBit Array$, Element%, Bit%

Where:

Array$ is a string that was previously set up to hold the Bit array, Element% is the desired element number to set, and Bit% holds either a 0 if the element is to be cleared, or anything else if it is to be set.

Comments:

SetBit is provided as a companion to DimBits and GetBit. Bit arrays are discussed in depth separately in this manual under the heading "Bit Arrays".

Example Location:

Project File   	Form/BASIC File	Procedure

BITARRAY.MAK   	BITARRAY.FRM	Bit_Click

Also see:

DimBits, GetBit


SortC, D, I, L, S

assembler subroutines contained in QPRO200.DLL

Purpose:

The five Sort routines are assembler Quick Sorts for ordering all or a portion of a numeric array.

Declare Syntax:

Declare Sub SortC Lib "QPRO200.DLL" (Array@, ByVal NumEls%, ByVal Direction%)

Declare Sub SortD Lib "QPRO200.DLL" (Array#, ByVal NumEls%, ByVal Direction%)

Declare Sub SortI Lib "QPRO200.DLL" (Array%, ByVal NumEls%, ByVal Direction%)

Declare Sub SortL Lib "QPRO200.DLL" (Array&, ByVal NumEls%, ByVal Direction%)

Declare Sub SortS Lib "QPRO200.DLL" (Array!, ByVal NumEls%, ByVal Direction%)

Call Syntax:

SortC Array@(Start%), NumEls%, Direction%

SortD Array#(Start%), NumEls%, Direction%

SortI Array%(Start%), NumEls%, Direction%

SortL Array&(Start%), NumEls%, Direction%

SortS Array!(Start%), NumEls%, Direction%

Where:

Array(Start%) is the first element to be included in the sort, NumEls% is the number of elements to consider, and Direction% indicates the sort direction. If Direction% is set to zero, then the sorting will be forward (ascending). Any other value will cause sorting to be performed backward (descending).

Comments:

Five separate routines are provided to allow you to add only the sort capabilities you need to your program. However, the Type sort routine (SortT) is capable of sorting all of the variable types, and you might consider using that if your program will need to sort more than one type of variable.

A complete description of these sorts, along with a comparison of indexed vs. normal sorting, is given elsewhere in this manual. Please see the section "Sorts vs. Indexed Sorts".

Example Location (SortC, SortD, SortI, SortL, and SortS):

     Project File    	Form/BASIC File    	Procedure

     NUMSORT.MAK      NUMSORT.FRM        	Command_Click

Also see:

ISortC, D, I, L, S, ISortStr and ISortStr2, ISortT and ISortT2, ISortV and ISortV2, SortStr and SortStr2, SortT and SortT2

SortV and SortV2


SortStr and SortStr2

assembler subroutines contained in QPRO200.DLL

Purpose:

SortStr will sort all or just a portion of a conventional (not fixed-length) string array into either ascending or descending order. SortStr2 is nearly identical, but sorting is performed without regard to capitalization.

Declare Syntax:

Declare Sub SortStr Lib "QPRO200.DLL" (Array$, ByVal NumEls%, ByVal Direction%)

Declare Sub SortStr2 Lib "QPRO200.DLL" (Array$, ByVal NumEls%, ByVal Direction%)

Call Syntax:

SortStr Array$(Start%), NumEls%, Direction%

SortStr2 Array$(Start%), NumEls%, Direction%

Where:

Array$(Start%) is the first element to be included in the sort, NumEls% is the number of elements to consider, and Direction% indicates the sort direction. If Direction% is set to zero, then the sorting will be forward (ascending). Any other value will cause sorting to be performed backward (descending).

Comments:

A description of these sorts, along with a comparison of indexed vs. normal sorting, is given elsewhere in this manual. Please see the section "Sorts vs. Indexed Sorts".

Example Location:

Project File     	Form/BASIC File    Procedure

STRSORT.MAK      	STRSORT.FRM        Sort_Click

Also see:

ISortC, D, I, L, S, ISortStr and ISortStr2, ISortT and ISortT2, ISortV and ISortV2, SortC, D, I, L, S, SortT and SortT2

SortV and SortV2


SortT and SortT2

assembler subroutines contained in QPRO200.DLL

Purpose:

SortT will sort all or part of a Type array into either ascending or descending order. SortT2 is nearly identical, but when considering the string component of a Type array, sorting is performed without regard to capitalization.

Declare Syntax:

Declare Sub SortT Lib "QPRO200.DLL" (Array As Any, ByVal NumEls%,_

       ByVal Direction%, ByVal ElSize%, ByVal MemberOffset%, ByVal MemberSize%)

Declare Sub SortT2 Lib "QPRO200.DLL" (Array As Any, ByVal NumEls%,_

       ByVal Direction%, ByVal ElSize%, ByVal MemberOffset%, ByVal MemberSize%)

Call Syntax:

SortT Array(Start%), NumEls%, Direction%, ElSize%, MemberOffset%, MemberSize%

SortT2 Array(Start%), NumEls%, Direction%, ElSize%,MemberOffset%, MemberSize%

Where:

Array(Start%) is the first element to be included in the sort, NumEls% is the number of elements to consider, and Direction% indicates the sort direction. If Direction% is set to zero, then the sorting will be forward (ascending). Any other value will cause sorting to be performed backward (descending).

ElSize% is the length of each Type element, MemberOffset% is the number of bytes into the Type where the key member being considered for the sort is located, and MemberSize% is its length. MemberSize% is coded using the convention described in the Comments section below.

Comments:

A special code is used to indicate the type of variable that is being operated upon. For example, sorting based on the string portion of a Type simply involves comparing the ASCII values for each character. However, sorting on a double precision component of a Type array requires a very different approach.

The type of variable being considered as the key is indicated to SortT and SortT2 as follows:

 0 = ByVal conventional string ($)

   	-1 = integer

  	-2 = long integer

   	-3 = single precision

   	-4 = double precision

   	-5 = currency

   	-6 = fuzzy single

   	-7 = fuzzy double

   	-8 = variant

   	+n = fixed-length string with a length of n bytes

Example Location:

     Project File   	Form/BASIC File	Procedure

     TYPESORT.MAK   	TYPESORT.FRM	Sort_Click

Also see:

ISortC, D, I, L, S, ISortStr and ISortStr2, ISortT and ISortT2, ISortV and ISortV2, SortC, D, I, L, S, SortStr and SortStr2

SortV and SortV2


SortV and SortV2

assembler functions in QPRO200.DLL

Purpose:

SortV will sort all or part of a Variant array into either ascending or descending order. SortV2 is nearly identical, but when considering strings, sorting is performed without regard to capitalization.

Declare Syntax:

Declare Function SortV% Lib "QPRO200.DLL" (Array as Variant, ByVal NumEls%, ByVal Direction%)

Declare Function SortV2% Lib "QPRO200.DLL" (Array as Variant, ByVal NumEls%, ByVal Direction%)

Call Syntax:

Failure% = SortV% (Array(Start%), NumEls%, Direction%)

Failure% = SortV2% (Array(Start%), NumEls%, Direction%)

Where:

Failure% is returned as a non-zero value if an error occurred.

Array(Start%) is the first element to consider in the sort; it also determines the mode of the sort.  See note

 2.

NumEls% is the total number of elements to consider.

Direction% is zero for an ascending sort, non-zero for descending.

Comments:

SortV will honor capitalization, while SortV2 will ignore it.

Attempting to sort on variants of type VT_EMPTY & VT_NULL will cause the routine to return True (-1) indicating an error.

If Empties or Nulls occur in the array they will be sorted in the following order:  VT_EMPTY, VT_NULL, variant types 2-8.

For a backwards sort, variant types 2-8, the order will be:  VT_NULL, VT_EMPTY.

NOTE 1:

When working with a Multi-dimensioned array the notion of consecutive elements becomes more of a challenge. The order of storage in memory is the controlling factor. Keep in mind that for Array(I, J, K, ... Z), consecutive elements will be stored at Array(I+1, J, K, ... Z) and Array(I+2, J, K, ... Z) etc. Therefore, Array(I, J, K, ... Z) and Array(I, J, K+1, ... Z) will be separated in memory by (IMax - IMin + 1) * (JMax - JMin + 1) elements (IMin & IMax are LBOUND & UBOUND of the indices), and (16 * elements) bytes.

NOTE 2:

The mode of the sort is defined as the type of comparisons to be performed on the individual data items.  comparisons will occur as though each element were of the same variable type as Array(Start%).

A description of these sorts, along with a comparison of indexed vs. normal sorting is given elsewhere in the manual.  Please see the section "Sorts vs. Indexed Sorts".

Also see:

ISortC, D, I, L, S, ISortStr and ISortStr2, ISortT and ISortT2, ISortV and ISortV2, SortC, D, I, L, S, SortStr and SortStr2

SortT and SortT2,


DOS Services

CDir

ClipFile

CSGetAttr

CSSetAttr

DCount

DiskInfo

DiskRoom

DiskSize

DOSCall

DOSError

DOSVer

ErrorMsg

ExeName

Exist

FClose

FCopy

FCount

FCreate

FEof

FFlush

FGet

FGetA

FGetAH

FGetR

FGetRT and FGetRTA

FGetT

FileComp

FileCopy

FileCrypt

FileInfo

FileSize

FileSort

FLInput

FLoc

FLof

FloppyType

FOpen and FOpenS

FOpenAll

FPut

FPutA

FPutAH

FPutR

FPutRT and FPutRTA

FPutT

FSeek

FStamp

GetDir

GetDrive

GetVol

GoodDrive

InterruptX

KillDir

KillFile

LastDrive

LineCount

LockFile

MakeDir

NameDir

NameFile

NetDrive

PutVol

QBLoad

QBSave

ReadDir

ReadDirs

ReadDirT

ReadFile

ReadFileI

ReadFileT

ReadFileX

ReadSect

ReadTest

Removable

ScanFile

SetDrive

SetError

Unique

UnLockFile

Valid

WhichError

WriteSect and WriteSect2

WriteTest


CDir

assembler subroutine contained in QPRO200.DLL

Purpose:

CDir serves the same purpose as Visual Basic's ChDir command, but it returns a code to indicate an error rather than requiring a separate error trapping procedure.

Declare Syntax:

Declare Sub CDir Lib "QPRO200.DLL" (ByVal NewDir$)

Call Syntax:

CDir NewDir$

Where:

NewDir$ is a string containing the new directory to change to.

Comments:

As with DOS, a new directory may be requested for either the current drive or any other legal drive in the system. For example, to change the directory on drive B when the current drive is C would be done like this:

     CDir "B:\NEWDIR"

Only two errors are likely when using CDir--either the specified directory does not exist, or an invalid drive was given. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.


ClipFile

assembler subroutine contained in QPRO200.DLL

Purpose:

ClipFile will establish a new length for the specified file, and the new length may be either shorter or longer than the number of bytes the file presently occupies.

Declare Syntax:

Declare Sub ClipFile Lib "QPRO200.DLL" (ByVal FileName$,  NewLength&)

Call Syntax:

ClipFile FileName$, NewLength&

Where:

FileName$ is the name of the file to process, and NewLength& is a long integer holding the new length the file is to be set to.

Comments:

One important use for ClipFile is to reduce the length of a file to which data has inadvertently been written. As an example, if Seek (or the QuickPak FSeek) is accidentally used to set the DOS pointer to a location beyond the end of a data file, the length of the file will automatically be extended. Another use for ClipFile is to truncate a database file to purge deleted records, as described below.  For ClipFile to work, the file being clipped must be closed.

Many database programs reserve an extra byte in each record just to indicate whether the record has been deleted or not. Unfortunately, the only way the deleted data can then be removed from the database is to copy only the active records to a new file one by one. Once the data has been copied the original file would be deleted, and the new file renamed.

However, there are several problems with that approach, the worst being that sufficient free disk space must be present to hold both the original data and the copy. A better method is to create a program to copy the deleted records to the end of the file, and then use ClipFile to set the new length to the end of the last active record. A program to accomplish this would be written much like a traditional bubble sort, except only a single pass would be required.

The only error that is likely is attempting to access a file that does not exist. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

Example Location:

Project File	Form/BASIC	File Procedure

FILEMOD.MAK	FILEMOD.FRM	ClipfileB_Click


CSGetAttr

assembler function contained in QPRO200.DLL

Purpose:

CSGetAttr examines a disk file and reports the setting of its DOS attribute byte.

Declare Syntax:

Declare Function CSGetAttr% Lib "QPRO200.DLL" (ByVal FileName$)

Call Syntax:

Attribute% = CSGetAttr%(FileName$)

Where:

FileName$ is the file being examined, and Attribute is assigned bit coded with the file's attributes. If the file does not exist or any other error occurs, the attribute variable will be set to -1.

Comments:

Every file has an attribute that is assigned at the time it is created. The attribute information is kept in a disk's directory, along with each file's name, date, and time. There are six possible attributes a file can possess, and in many cases it can have more than one. An assembler program can establish any attribute when it asks DOS to create a file, however Visual Basic offers much less flexibility.

The most common file attribute is Archive, and this simply indicates if the file has been backed up since the last time it was modified. If the archive portion of the attribute byte is set to 1 (true), then the file has been modified but not backed up. DOS sets the archive bit every time a file is written to, and most backup programs clear it as they process each file. Files that are created by Visual Basic have the archive attribute only.

The next attribute is Subdirectory, and this indicates that a file is a DOS subdirectory, as opposed to a program or data file. There is no real difference between a subdirectory and any other file, other than the setting of this bit in its attribute byte.

The third type of attribute is Volume Label. Of course a volume label isn't actually a file, but rather a

name that may be given to a diskette or hard disk. However, a disk's volume label is kept in the root

directory along with the other file entries.

The final three attributes are System, Hidden, and Read- Only. The most common system files are IBMBIO.COM and IBMDOS.COM, though any file could be defined as a system file. Hidden files are not displayed by the Dir or Files commands, and a file that has been marked as read-only may not be altered. Setting a file to read-only is a good way to prevent it from being accidentally overwritten or erased.  The attribute returned by CSGetAttr is in the form of a single byte, and the placement of the various bits is illustrated in the table above.

To test an individual bit, you can use code such as the following:

     Status = Attribute And 2^Bit

Besides returning an attribute of -1 if the named file is not present, the DOSError and WhichError functions may also be examined for error information.

Also provided with QuickPak Professional for Windows is a companion program SetAttr that allows setting a file's attribute.

See Also:	CSSetAttr


CSSetAttr

assembler subroutine contained in QPRO200.DLL

Purpose:

CSSetAttr sets the attribute byte for a specified file.

Declare Syntax:

Declare Sub CSSetAttr Lib "QPRO200.DLL" (ByVal FileName$, ByVal Attr%)

Call Syntax:

CSSetAttr FileName$, Attr%

Where:

FileName$ is the file being examined, and Attribute% is bit coded with the attributes to set it to.

Comments:

Every file has an attribute that is assigned at the time it is created. The attribute information is kept in a disk's directory, along with each file's name, date, and time.

The table below shows some common values for the Attribute% variable:

    1         Read-Only

    2         Hidden

    32       Archive

    0         turns off all attributes

A complete description of file attributes is given in the section that describes the CSGetAttr function.  To set a bit, use the following code:

   Attribute% = Attribute% Or 2^ Bit%


DCount

assembler function contained in QPRO200.DLL

Purpose:

DCount will report the number of directories under the current directory that match a particular

specification.

Declare Syntax:

Declare Function DCount% Lib "QPRO200.DLL" (ByVal DirSpec$)

Call Syntax:

Count% = DCount%(DirSpec$)

Where:

DirSpec$ holds a DOS directory name specification, and Count% receives the number of matching directories.

Comments:

DCount is intended to provide a count of the directories on a disk, in preparation for using ReadDir to obtain a list of all their names.

Most people think of the DOS wild cards (? and *) as being applicable only to file names, however they are also intended to be used with directory names. For example, to use DCount to determine the number of directories that are under the root directory, you would specify a search criteria such as "\*.*" when using DCount.

Also See:

Where FCount provides a count of file names that match a given specification, DCount instead searches for directory names. It is important not to confuse these two routines.

Example Location:

Project File	Form/BASIC File	Procedure

READDIR.MAK	READDIR.FRM	Command1_Click


DiskInfo

assembler subroutine contained in QPRO200.DLL

Purpose:

DiskInfo calls on DOS to examine a disk, and reports its sector and cluster makeup.

Declare Syntax:

Declare Sub DiskInfo Lib "QPRO200.DLL" (ByVal Drive$, Bytes%, Sectors&, _

  FreeClusters&, TotalClusters&)

Call Syntax:

DiskInfo Drive$, Bytes%, Sectors&, FreeClusters&, TotalClusters&

Where:

Drive$ contains the letter of the drive to examine, and Bytes% returns how many bytes each sector holds. Sectors% contains the number of sectors that comprise each disk cluster, FreeClusters% tells how many of them are available, and TotalClusters% indicates the total disk capacity.

Comments:

As with all of the QuickPak Professional for Windows DOS services, Drive$ may be either upper or lower case, or a null string to indicate the default drive. Since only the first character of Drive$ is examined, you may also pass a complete file name to DiskInfo if that is more convenient:

   Drive$ = "C:WHATEVER.XYZ"

The number of Bytes per sector will almost always be 512 under DOS, though the number of sectors per

cluster will vary depending on the type of disk being examined. For example, a 360K floppy disk always

stores two sectors within each cluster, while a 1.2 MB floppy allocates only one sector per cluster. Thus,

a program can quickly and easily determine what type of disk drive it is dealing with.

The amount of free space on a disk may easily be calculated by multiplying the various components that DiskInfo returns.

See Also:

The DiskRoom and DiskSize functions.

Example Location:

Project File	Form/BASIC File	Procedure

DISKINFO.MAK	DISKINFO.FRM	Command1_Click


DiskRoom

assembler function contained in QPRO200.DLL

Purpose:

DiskRoom returns the number of bytes that are currently available on a specified disk drive.

Declare Syntax:

Declare Function DiskRoom& Lib "QPRO200.DLL" (ByVal Drive$)

Call Syntax:

Room& = DiskRoom&(Drive$)

Where:

Drive$ contains either an upper or lower case letter for the disk drive being examined, or is null to indicate the default drive. Room& is assigned the number of available bytes.

Comments:

Also see the DiskSize function which returns a disk's total storage capacity.

Example Location:

Project File 	Form/BASIC File	Procedure

DISKINFO.MAK	DISKINFO.FRM	Command1_Click


DiskSize

assembler function contained in QPRO200.DLL

Purpose:

DiskSize returns the total capacity in bytes of a specified disk drive.

Declare Syntax:

Declare Function DiskSize& Lib "QPRO200.DLL" (ByVal Drive$)

Call Syntax:

Size& = DiskSize&(Drive$)

Where:

Drive$ contains an upper or lower case letter for the disk drive being examined, or is null to indicate the default drive. Size& is assigned the total number of bytes the disk can hold.

Comments:

Also see the DiskRoom function which returns the amount of free space remaining on a disk.

Example Location:

Project File 	Form/BASIC File 	Procedure

DISKINFO.MAK	DISKINFO.FRM	Command1_Click


DOSCall

assembler subroutine contained in QPRO200.DLL

Purpose:

DOSCall is a wrapper for the Windows API routine DOS3Call for calling DOS Interrupt &H21. DOSCall

uses a type structure to fill the appropriate registers.

Declare Syntax:

Declare Sub DosCall Lib "QPRO200.DLL" (Registers as REGTYPE)

Call Syntax:

Type REGTYPE

  RegAX  As Integer

  RegBX  As Integer

  RegCX  As Integer

  RegDX  As Integer

  RegBP  As Integer

  RegSI  As Integer

  RegDI  As Integer

  Flags  As Integer

  RegDS  As Integer

  RegES  As Integer

End Type

Dim Registers As RegType

DosCall Registers

Where:

The Registers Type variables is filled with service and other needed values in preparation for calling standard DOS services such as getting the system date.

Comments:

Most DOS services can be called with the exception of FCB functions that are not supported by Windows.

Services that may result in an error return with the Carry bit in the Flags register set to indicate that an

error code is in the AX register, or clear if no error occurred.  You can test the Carry bit like this:

 If Registers.Flags And 1 Then

 Print "Error code"; Registers.AX

Else

 Print "No error"

End If

When Visual Basic accesses DOS, it sets a flag within Windows telling it that errors are to be returned to Visual Basic, rather than have Windows display a dialog box.  Therefore, unless you change this flag errors are reported using the Carry flage and the value in AX.  If you prefer to have Windows display the dialog box you can change this flag using the following code:

Declare Function SetErrorMode% Lib "Kernel" (Byval Mode%)

NewMode% = 0   '0 = let windows handle it

OldMode% = SetErrorMode(NewMode%) 'set mode

' --- do program stuff ---

...

...

Dummy% = SetErrorMode(OldMode%) 'reset mode to original


DOSError

assembler function contained in QPRO200.DLL

Purpose:

DOSError reports if an error occurred during the last call to a QuickPak Professional for Windows DOS routine.

Declare Syntax:

Declare Function DOSError% Lib "QPRO200.DLL" ()

Call Syntax:

If DosError%() Then Print "A DOS error occurred"

Where:

DOSError% returns 0 if there was no error, or -1 if there was.

Comments:

All of the QuickPak Professional for Windows routines assign a value to the DOSError and WhichError functions to indicate their success or failure. Rather than requiring you to set up a separate error handling procedure and use On Error, you can simply query these functions after performing any QuickPak Professional for Windows DOS operation. DOSError is discussed in the section entitled "Eliminating On Error".

See Also:

The complimentary function WhichError.

Example Location:

Project File	Form/BASIC File	Procedure

FILEMOD.MAK	FILEMOD.FRM	FCopyB_Click


DOSVer

assembler function contained in QPRO200.DLL

Purpose:

DOSVer returns the version of DOS that is presently running on the host PC.

Declare Syntax:

Declare Function DosVer% Lib "QPRO200.DLL" ()

Call Syntax:

Version! = DosVer%() / 100

Major% = DosVer%() \ 100

Minor% = DosVer%() Mod 100

Where:

Version! is assigned the DOS version number. The major and minor version components may also be determined as shown in the example above.

Comments:

Internally, the DOS service that reports the version number returns two separate values_the major version and the minor version. For example, if a PC is using DOS 3.10, then the major version would be 3, and the minor version 10.

DOSVer simply combines the two into a single value, and then multiplies the result times 100. This approach is used to eliminate having to deal with floating point arithmetic in assembly language, which is a genuine pain to put it mildly.

There are a number of situations where DOSVer will come in handy. One would be if you are writing a program meant for use on a network, which of course requires DOS version 3.0 or later.

See Also:

WinVer% (Windows version)

Example Location:

Project File	Form/BASIC File	Procedure

SYSINFO.MAK	SYSINFO.FRM	Form_Click


ErrorMsg

assembler function contained in QPRO200.DLL

Purpose:

ErrorMsg returns an appropriate message given any of the Visual Basic error numbers for a DOS service.

Declare Syntax:

Declare Function ErrorMsg$ Lib "QPRO200.DLL" (ByVal ErrorNumber%)

Call Syntax:

Message$ = ErrorMsg$(ErrorNumber%)

Where:

ErrorNumber% is a valid Visual Basic error number for a DOS operation.

Comments:

Regardless of how you intend to handle DOS and other errors in your programs, at some point you will probably need to print a message to indicate what went wrong. ErrorMsg provides an easy way to add the standard Visual Basic error messages without requiring Error, or the wasted string space that results from storing the messages as text constants.

The text for each message is kept in a table in the QPRO200.DLL, and is organized such that it may be easily modified or expanded. This is shown in the ErrorMsg assembler source code.

We have purposely omitted the "normal" Visual Basic errors such as Illegal Function Call and Overflow, though these could be added by modifying the assembler source code. However, two Visual Basic error messages that have been included are "Out of string space" and "Out of memory".

Example Location:

Project File 	Form/BASIC File	Procedure

DISKINFO.MAK	DISKINFO.FRM	Command1_Click

Also See:

WhichError


ExeName

assembler function contained in QPRO200.DLL

Purpose:

ExeName returns the full name of the currently executing program, including the drive, path, and file

name.

Declare Syntax:

Declare Function ExeName$ Lib "QPRO200.DLL" ()

Call Syntax:

FullName$ = ExeName$()

Where:

FullName$ receives a string such as "C:\QB\MYPROG.EXE".

Comments:

There are several situations in which it is useful to know the full name of a program. The most common is when the program manages one or more data files, or maintains a file of configuration information.ExeName provides a simple way to determine where the currently running program resides, by searching the program's environment table for the drive, path, and file name. This information is placed there by DOS when a program is first run, thereby reducing the amount of work that ExeName must do.

If the program is located in the current directory or an explicit directory name was given when the program was started, then the drive letter and colon will be included. However, there is one situation in which ExeName will not return the drive as part of the name.

If the program is not in the current directory but was instead found by DOS via the current PATH setting, and the path does not include a drive specifier, then DOS will not place the drive into the program's environment.

The examples below show what ExeName will return for a program named MYPROG.EXE that is located in the \UTIL directory, when \UTIL is not the current directory.

   PATH=\UTIL            " \UTIL\MYPROG.EXE"

   PATH=C:\UTIL          " C:\UTIL\MYPROG.EXE"

Therefore, if your program lets the operator change the current drive, you should use the GetDrive function early in the program to obtain the complete path information. An example of this is shown below.

   N$ = ExeName$

   If InStr(N$, ":") = 0 Then N$ = Chr$(GetDrive%) + ":" + N$

Example Location:

Project File	Form/BASIC File 	Procedure

SYSINFO.MAK	SYSINFO.FRM	Form_Click


Exist

assembler function contained in QPRO200.DLL

Purpose:

Exist will quickly determine the presence of a file.

Declare Syntax:

Declare Function Exist% Lib "QPRO200.DLL" (ByVal FileName$)

Call Syntax:

There% = Exist%(FileName$)

Where:

FileName$ is the file or file specification whose presence is being determined, and There% is assigned either to -1 if the file exists, or 0 if it does not.

The FileName$ parameter may optionally contain a drive letter, a directory path, and either of the DOS wild cards. For example, "B:\STUFF\*.BAS" would tell if any Visual Basic program files are present on drive B in the \STUFF directory.

Comments:

The main purpose of Exist is to prevent the error caused by attempting to open a file for input when it does not exist. Rather than having to set up an On Error trap just prior to each attempt to open a file, Exist will directly tell if the file is present.

Example Location:

Project File 	Form/BASIC File 	Procedure

FILEMOD.MAK	FILEMOD.FRM	Command1_Click


FClose

assembler subroutine contained in QPRO200.DLL

Purpose:

FClose will close a file that had been previously opened with the QuickPak Professional for Windows FOpen command.

Declare Syntax:

Declare Sub FClose Lib "QPRO200.DLL" (ByVal Handle%)

Call Syntax:

FClose Handle%

Where:

Handle% is the DOS file handle that was assigned when the file was first opened.

Comments:

The only error that is likely to happen when using FClose would be caused by giving it an invalid file handle. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

DOS handles and file access using the QuickPak Professional for Windows routines are discussed elsewhere in this manual in the section entitled "Eliminating On Error".

Example Location:

Project File 	Form/BASIC File 	Procedure

FILEMOD.MAK	FCOMP.FRM	FileComp


FCopy

assembler subroutine contained in QPRO200.DLL

Purpose:

FCopy will copy a file from within a Visual Basic program without requiring the use of Shell.

Declare Syntax:

Declare Sub FCopy Lib "QPRO200.DLL" (ByVal Source$, ByVal Dest$, ErrCode%)

Call Syntax:

FCopy Source$, Dest$, ErrCode%

Where:

Source$ is the source file name, Dest$ is the target file name, and ErrCode% indicates if an error that

occurred was on the source or destination file.

Comments:

FCopy is a vastly superior method for copying files compared to using the Shell command. Besides being much cleaner in general, using Shell introduces a number of problems.  One major limitation of Shell is that it internally runs a second copy of COMMAND.COM which requires sufficient available memory, and also means that COMMAND.COM must be available. It is important that both the source and destination variables be complete file names. That is, Dest$ may not be given simply as "A:" or "C:\STUFF\". Likewise, Source$ may not contain DOS wild cards to indicate multiple files.

If the QuickPak Professional for Windows DOSError function is zero, then the copying was successful and you can safely ignore ErrCode%. Otherwise, if an error occurred processing the source file, then ErrCode% will be returned set to 1. A value of 2 indicates a problem with the destination file. In either case, the QuickPak Professional for Windows WhichError% function should be used to determine the type of error that occurred.

See Also:

The related routine FileCopy.

Example Location:

Project File 	Form/BASIC File 	Procedure

FILEMOD.MAK	FILEMOD.FRM	FCopyB_Click


FCount

assembler function contained in QPRO200.DLL

Purpose:

FCount will report the number of file names that match a particular specification.

Declare Syntax:

Declare Function FCount% Lib "QPRO200.DLL" (ByVal FileSpec$)

Call Syntax:

Count% = FCount%(FileSpec$)

Where:

FileSpec$ holds a DOS file name or specification, and Count% receives the number of matching entries.

Comments:

FCount is intended to provide a count of the files on a disk, in preparation for using ReadFile to obtain a list of all their names.

You will generally use the DOS wild cards with FCount, for example "*.BAS" would provide a count of all the Visual Basic program files in the current directory on the default disk. Of course, FCount will also accept a drive letter or path name as well:

   Count = FCount%("D:\LOTUS\*.WK1")

See Also:

Where DCount provides a count of directory names that match a given specification, FCount instead searches for file names. It is important not to confuse these two routines.

Example Location:

Project File 	Form/BASIC File 	Procedure

FILEMOD.MAK	FILEMOD.FRM	Command1_Click


FCreate

assembler subroutine contained in QPRO200.DLL

Purpose:

FCreate is used to create a file in preparation for writing to it with the QuickPak Professional for Windows file handling routines.

Declare Syntax:

Declare Sub FCreate Lib "QPRO200.DLL" (ByVal FileName$)

Call Syntax:

FCreate FileName$

Where:

FileName$ is the name of the file to be created.

Comments:

FCreate serves the same purpose as the Visual Basic Open For Output command followed immediately by a Close. That is, if the file does not exist it will be created, and if it is already present it will be truncated to a length of zero bytes.

As with all of the QuickPak Professional for Windows file services, FCreate will also accept an optional drive letter and/or directory path. However, a wild card is not permitted.FCreate will not cause an error if the disk is full, because it does not attempt to write any information to the disk_it merely establishes a directory entry for the file. In fact, if the file already exists and it contains data, FCreate will instead free up the disk space that had been occupied.

There are two probable causes for an error to occur when using FCreate. Either an invalid file name was given (it contains a wild card or refers to an invalid directory), or the disk's directory is full. For example, a 360K floppy disk can accommodate only 112 entries in its root directory, even if the data area is not filled up.

Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError

functions.


FEof

assembler function contained in QPRO200.DLL

Purpose:

FEof will report if the current DOS Seek location is at the end of the specified file.

Declare Syntax:

Declare Function FEof% Lib "QPRO200.DLL" (ByVal Handle%)

Call Syntax:

If FEof%(Handle%) Then . . .

Where:

Handle% is the handle that DOS assigned when the file was first opened using FOpen.

Comments:

FEof serves the same purpose as Visual Basic's EOF function, except it is intended for use with files that are being manipulated using the QuickPak Professional for Windows file handling routines.FEof returns -1 if the current DOS Seek location is at the end of the file, or 0 if it is not.  This allows you to test for an EOF condition either with or without the Visual Basic Not command, as shown below.

If FEof%(Handle%) Then

  Print "It's at the end"

End If

or

 If Not FEof%(Handle%) Then

  Print "Not at the end"

End If

If an invalid handle is given, the DOSError and WhichError functions will be set to indicate the error condition.


FFlush

assembler subroutine contained in QPRO200.DLL

Purpose:

FFlush will flush a file's data buffers to disk without requiring the file to be closed.

Declare Syntax:

Declare Sub FFlush Lib "QPRO200.DLL" (ByVal Handle%)

Call Syntax:

FFlush Handle%

Where:

Handle% is the file handle that DOS assigned when the file was first opened.

Comments:

When data is read from or written to disk, it is always first passed through an area of memory called a file buffer. The total size of the buffer is determined by the setting of the BUFFERS= statement in the CONFIG.SYS file. If CONFIG.SYS is not present, then the number of buffers defaults to 3 for an AT. Each buffer comprises 512 bytes of memory, which is the size of one disk sector.

Buffers are an important factor in speeding up the operation of a PC, because it allows information that has previously been read or written to be accessed again later without having to actually read it from the disk. Further, by always reading an entire sector rather than only the number of bytes an application requests, subsequent sequential reads will not require DOS to physically access the disk again.

However, one problem with buffering disk writing is that the information is not written to the disk at the time the write is performed. Rather, the information sits there in memory until the buffer becomes full, or the file is closed. If your program has just used Print or Put to write data to a disk file and a power outage occurs, the data will never be transferred to the file.

FFlush allows you to force the file buffer's contents to be written to the disk, but without having to close the file and then re-open it again.

Unfortunately, FFlush will work only with files that have been opened and written to using the QuickPak Professional for Windows file routines. It will not flush the buffers for a file written using Visual Basic's file commands, because Visual Basic performs additional buffering before sending the data to DOS.

The only error that is likely with FFlush is giving it an invalid handle number. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.


FGet

assembler subroutine contained in QPRO200.DLL

Purpose:

FGet reads data from a disk file in a manner similar to Visual Basic's binary Get command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub FGet Lib "QPRO200.DLL" (ByVal Handle%, Destination$)

Call Syntax:

FGet Handle%, Destination$

Where:

Handle% is the DOS file handle that was assigned when the file was opened, and Destination$ is the string that is to receive the data. The length of Destination$ determines how many bytes are to be read.

Comments:

FGet reads data from the specified file at the location held in the DOS file pointer. The current pointer location is established by the most recent read or write operation, or by using the Visual Basic Seek command or the QuickPak Professional for Windows FSeek subroutine.The length of Destination$ is used to tell FGet how many bytes it is to read to insure that sufficient room has been set aside. If FGet had been written to expect a separate variable to specify the number of bytes, it would be possible to corrupt string memory by failing to first assign the string to a sufficient length.

Only two errors are likely when using FGet either the handle number was invalid, or the destination string was null. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

See Also:

The companion routine FGetT, reads the file data into a Type variable instead of a string.


FGetA

assembler subroutine contained in QPRO200.DLL

Purpose:

FGetA is similar to the QuickPak Professional for Windows FGetT routine, however it is usually used to load an entire array in one operation.

Declare Syntax:

Declare Sub FGetA Lib "QPRO200.DLL" (ByVal Handle%, Array As Any, NumBytes&)

Call Syntax:

FGetA Handle%, Array(Element%), NumBytes&

Where:

Handle% is the DOS file handle that was assigned when the file was opened, and Array(Element%) is any array (except a string array) that is to receive the data.NumBytes& indicates the number of bytes to be read.

Comments:

FGetA reads data from the specified file at the location held in the DOS file pointer. The current pointer location is established by the most recent read or write operation, or by using the Visual Basic Seek command or the QuickPak Professional for Windows FSeek subroutine.

FGetA is intended to serve a purpose similar to QuickPak Professional for Windows QBLoad. As described in the sections "Saving Arrays to Disk", loading an entire file in one operation can provide a tremendous improvement in speed. However, in order to BLoad a file, it must have been originally saved in the special format that was used. FGetA instead loads any data file, without requiring the special QBSave header.

Only two errors are likely when using FGetA--either the handle number was invalid, or the number of bytes specified was zero. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

This subroutine is not applicable to arrays of variants.

See Also:

The companion routine FPutA writes a file from an array.

Example Location:

Project File 	Form/BASIC File 	Procedure

FILEMOD.MAK	FCOMP.BAS	FileComp


FGetAH

assembler subroutine contained in QPRO200.DLL

Purpose:

FGetAH will retrieve an entire array from disk in a single operation.

Declare Syntax:

Declare Sub FGetAH Lib "QPRO200.DLL" (ByVal FileName$, Array As Any, ByVal _

 ElSize%, ByVal NumEls%)

Call Syntax:

FGetAH FileName$, Array(Start%), ElSize%, NumEls%

Where:

FileName$ is the file to be loaded, and Array(Start) is where in memory to load the file.

ElSize% indicates the length of each array element in bytes (or a special code to indicate the length),

and NumEls% is the number of elements to be loaded.

Comments:

Unlike the other FGet routines that expect a handle to a file that has already been opened, FGetAH assumes you want to load the entire file at once. This eliminates the extra steps of having to first open the file, remember the handle, load the data, and finally close the file. FGetAH is used like the QuickPak Professional for Windows QBLoad routine, except it is not limited to loading 64K or less. FGetAH also accepts the same size code that is used by the QuickPak Professional for Windows Type sort routines:

     -1 = 2-byte integer

     -2 = 4-byte long integer         } these codes are

     -3 = 4-byte single precision    } interchangeable

     -4 = 8-byte double precision

     -5 = 8-byte Currency

If an error occurs while reading a file, the DOSError and WhichError functions will be set appropriately.  This subroutine is not applicable to arrays of variants.

See Also:

The related routine FPutAH writes an entire array.


FGetR

assembler subroutine contained in QPRO200.DLL

Purpose:

FGetR reads data from a disk file in a manner similar to Visual Basic's random Get command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub FGetR Lib "QPRO200.DLL" (ByVal Handle%, Destination$, RecNumber&)

Call Syntax:

FGetR Handle%, Destination$, RecNumber&

Where:

Handle% is the DOS file handle that was assigned when the file was opened, Destination$ is the string that is to receive the data, and RecNumber& is a long integer that indicates the record to be read.  The length of Destination$ determines how many bytes are to be read, and is also used with RecNumber& to determine how far into the file the record is located.

Comments:

The length of Destination$ tells FGetR how many bytes it is to read to insure that sufficient room has been set aside. If FGetR had been written to expect a separate variable to specify the number of bytes, it would be possible to corrupt string memory by failing to first assign the string to a sufficient length.Only two errors are likely to occur when using FGetR--either the handle number was invalid, or the destination string was null. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

See Also:

The companion routine FGetRT, reads the data into a Type variable instead of a string.


FGetRT and FGetRTA

assembler subroutines contained in QPRO200.DLL

Purpose:

FGetRT and FGetRTA read data from a disk file in a manner similar to Visual Basic's random Get command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub FGetRT Lib "QPRO200.DLL" (ByVal Handle%, Destination As Any, RecNumber&, ByVal _

     RecSize%)

Declare Sub FGetRTA Lib "QPRO200.DLL" (ByVal Handle%, Destination  As Any, RecNumber&, _

     ByVal RecSize%)

Call Syntax:

FGetRT Handle%, Destination, RecNumber&, RecSize%

or

FGetRTA Handle%, Destination(Start%), RecNumber&, RecSize%

Where:

Handle% is the DOS file handle that was assigned when the file was opened, and Destination is Type variable that is to receive the data. RecNumber& indicates the record to be read, and RecSize% is the length in bytes of each record. RecSize% determines how many bytes are to be read, and is also used internally by FGetRT and FGetRTA to determine how far into the file the record is located.

Comments:

FGetRT and FGetRTA are nearly identical, except that FGetRTA is usually used to load one or more elements at once directly into an array. Because it is up to the program to tell FGetRT how many bytes are to be read, it is very important that sufficient room has been first set aside in the destination variable. If this is not done, string memory will probably be corrupted.

Only two errors are likely to occur when using FGetRT_either the handle number was invalid, or the record size was given as zero. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions. Also see FGetR, which reads the data into a conventional string variable.

The type of variable being considered as the key is indicated to FGetRT and FGetRTA as follows:

 -1 = integer

 -2 = long integer

 -3 = single precision

 -4 = double precision

 -5 = currency

Example Location (FGetRT):

Project File 	Form/BASIC File 	Procedure

GPREC.MAK	GPREC.FRM	Command4_Click


FGetT

assembler subroutine contained in QPRO200.DLL

Purpose:

FGetT reads data from a disk file in a manner similar to Visual Basic's binary Get command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub FGetT Lib "QPRO200.DLL" (ByVal Handle%, Destination As Any, NumBytes&)

Call Syntax:

FGetT Handle%, Destination, NumBytes&

Where:

Handle% is the DOS file handle that was assigned when the file was opened, Destination is the Type

variable that is to receive the data, and NumBytes% indicates how many bytes are to be read.

Comments:

FGetT reads data from the specified file at the location held in the DOS file pointer. The current pointer location is established by the most recent read or write operation, or by using the Visual Basic Seek command or the QuickPak Professional for Windows FSeek subroutine.

Because it is up to the program to tell FGetT how many bytes are to be read, it is very important that sufficient room has been first set aside in the destination variable. If this is not done, string memory will probably be corrupted.

Only two errors are likely when using FGetT--either the handle number was invalid, or the number of bytes to be read was specified as zero. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

See Also:

The companion routine FGet reads the data into a conventional string variable instead of a Type.


FileComp

Visual Basic function contained in FILECOMP.BAS

Purpose:

FileComp will report if any two disk files are the same.

Syntax:

Same = FileComp%(File$, File2$, ErrCode%)

Where:

File1$ and File2$ are the two files being compared, and Same receives either -1 if they are the same, or 0 if they are not.

If a DOS error occurs (file not found, drive door open, etc.), then ErrCode% will tell which file the error relates to. An ErrCode% of 1 means the problem was with File1$, and 2 means it was with File2$.

Comments:

Besides returning a true or false condition based on a comparison of the two files, FileComp also uses the DOSError and WhichError services to report disk errors.

Example Location:

Project File 	Form/BASIC File 	Procedure

FILEMOD.MAK	FILEMOD.FRM	FileCompB_Click


FileCopy

Visual Basic subprogram contained in FILECOPY.BAS

Purpose:

FileCopy is intended to serve as a "front end" to the FCopy routine, and allows it to accepts wild cards in the source file specification.

Syntax:

FileCopy Source$, Dest$, Copied%, ErrCode%

Where:

Source$ is a file specification such as "*.BAS", and Dest$ is the target drive or directory. Copied% returns holding the number of files actually copied.  If an error occurs during the copying process, ErrCode% indicates if the problem was with the source or destination file.

Comments:

FileCopy augments the assembler FCopy routine by allowing a group of files to be copied to a new drive or directory. Where FCopy does not allow wild cards in the source file name or a destination drive or path only, FileCopy permits both.

FileCopy uses the FCount and ReadFile routines to obtain a list of all the matching files, and then copies them one by one to the destination. In fact, because FileCopy processes multiple files, you must not include a specific target file name.

A typical source and destination specification would be:

Source$ = "C:\SUBDIR\FILE*.*"

Dest$ = "B:"

or

   Dest$ = "\SUBDIR"

or

   Dest$ = "A:\"

Only two errors are likely when using FileCopy--either no files were found that matched the file specification, or the source or destination drive/directory were invalid. If the copying is successful, ErrCode% will hold a value of 0.  If an error is encountered, ErrCode% will hold either a 1 or 2 specifying source or Dest file respectively.  To get a more detailed error code, use WhichError() and ErrorMsg$().

Example Location:

Project File       	Form/BASIC File 	Procedure

FILECOPY.MAK	FILECOPY.FRM	CopyPB_Click


FileCrypt

Visual Basic subprogram contained in FILECRPT.BAS

Purpose:

FileCrypt will quickly encrypt any file using a password provided by the calling program.

Syntax:

Call FileCrypt(FileName$, PassWord$)

Where:

FileName$ is the name of the file being encrypted, and PassWord$ is the password that is to be used.

Comments:

FileCrypt will be used both to encrypt a file and to decrypt it again later. However, you must provide the identical password each time FileCrypt is called. Capitalization is important, as well as leading and trailing spaces.

FileCrypt uses the QuickPak Professional for Windows Encrypt2 routine to actually process the file's contents. If the file is too long to fit into memory all at once, it will be processed in pieces.Rather than simply applying the password you provide when encrypting the file, FileCrypt first encrypts the password against an internal string of nonsense characters. This provides an extra margin of safety in case the file contains a long series of blanks or zero bytes. Blank spaces and Chr$(0) zero bytes could possibly reveal the password if the file is carefully examined.

For the best protection, you should select a password that is at least five characters in length, because the same password is used repeatedly to encode the file's contents. Since the main objective is to prevent the file from containing any recognizable pattern, a longer password will be repeated less often. See the discussion that accompanies the Encrypt routine for more information about passwords.The only error that is likely when using FileCrypt is that the specified file does not exist. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

Example Location:

Project File  	Form/BASIC File 	Procedure

FLCRYPT.MAK	FLCRYPT.FRM	FCryptPB_Click


FileInfo

assembler subroutine contained in QPRO200.DLL

Purpose:

FileInfo returns all of the characteristics of a file, including its date, time, size and attributes.

Declare Syntax:

Declare Sub FileInfo Lib "QPRO200.DLL" (ByVal FileName$, TypeVar As FINFO)

Call Syntax:

FileInfo FileName$, TypeVar

Where:

FileName$ is the name of the file to report on, and TypeVar is a Type variable that will receive the

information.

Comments:

If the file doesn't exist, the Year portion of the Type variable will be set to zero. The DOSError and

WhichError functions may also be examined to see if an error occurred. If the file name contains wild

cards (* or ?), then the first file matching the specification will be reported on. The Type variable must be

set up like this:

Type FINFO

 Year	As Integer

 Month	As Integer

 Day  	As Integer

 Hour  	As Integer

 Minute 	As Integer

 Second 	As Integer

 Size   	As Long

 Attrib 	As Integer

End Type

The file's attribute is bit-coded as follows:

 0            0  1    1    1     1      1      1

Archive                                        Read Only

Subdirectory                                   Hidden

Volume Label                                   System

Two routines are also included with QuickPak Professional for Windows to retrieve only the size of a file.

FileSize expects a file name as a string, while FLof accepts a DOS handle for a file that is currently

open. Also see the GetAttr function for a discussion of the file attribute byte.

Example Location:

Project File 	Form/BASIC File 	Procedure

FILEINFO.MAK	FILEINFO.FRM	Command1_Click


FileSize

assembler function contained in QPRO200.DLL

Purpose:

FileSize will quickly return the length of a named file.

Declare Syntax:

Declare Function FileSize& Lib "QPRO200.DLL" (ByVal FileName$)

Call Syntax:

Size& = FileSize&(FileName$)

Where:

FileName$ is the name of the file, and Size is assigned its length in bytes. If the file does not exist Size is instead assigned a value of -1.

Comments:

Besides returning a size of -1 if the named file is not present, the DOSError and WhichError functions may also be examined for error information.

See Also:

See the description for FLof which returns a file's size, but it accepts a file handle instead of a name.


FileSort

Visual Basic subprogram contained in FILESORT.BAS

Purpose:

FileSort will sort a random access disk file on any number of keys, and each key may be ordered

independently either ascending or descending.

Syntax:

CALL FileSort(DataFile$, IndexFile$, First&, Last&, Table%(), RecLength%)

Where:

DataFile$ is the name of the data file being sorted, IndexFile$ is the name of the index file that will be created, and First& and Last& indicate the range of records to be sorted. If both First& and Last& are zero, then the entire file will be considered. The Table%() array tells FileSort which record fields are to be used as the sort keys, using the same method as KeySort.  RecLength% is the length of each disk record in bytes.  If RecLength% is given as a negative value, sorting is performed without regard to capitalization.

Comments:

When FileSort is called, it does not actually sort the data file. Rather, a separate index file is created which holds a list of record numbers in sorted order.  FileSort calls upon KeySort to do the actual sorting. Therefore, the same method of creating the Table array is used here. Please see the KeySort description for more information.

Disk errors may be detected by using the DOSError and WhichError functions.


FLInput

assembler function contained in QPRO200.DLL

Purpose:

FLInput will read a line of input from a file that has been opened using the QuickPak Professional for Windows FOpen routine.

Declare Syntax:

Declare Function FLInput$ Lib "QPRO200.DLL" (ByVal Handle%, Buffer$)

Call Syntax:

Work$ = FLInput$(Handle%, Buffer$)

Where:

Handle is the file handle that was assigned when the file was first opened, and Buffer$ is a temporary work string needed by FLInput as it reads the file contents.

Comments:

Unlike the other QuickPak Professional for Windows binary input routines that read a specified number of bytes, words, or elements from a file, FLInput reads until it encounters the Chr$(13) that marks the end of a line, or the Chr$(26) that indicates the end of a file. For files that do not contain an end of file marker, FLInput stops reading at the physical end of the file.The only errors that are likely when using FLInput are "Input Past End" (number 62) or "Buffer Too Small" (number 83). It is up to the calling program to provide a temporary work buffer for FLInput. If that buffer is too small to read an entire line, you should invoke FLInput again to obtain the remainder.

Example Location:

Project File 	Form/BASIC File 	Procedure

SEQFILE.MAK	SEQFILE.FRM	Command2_Click


FLoc

assembler function contained in QPRO200.DLL

Purpose:

FLoc reports the current DOS file pointer position for files that have been opened using the QuickPak

Professional for Windows file services. FLoc serves the same purpose as Visual Basic's built-in Loc()

function.

Declare Syntax:

Declare Function Floc& Lib "QPRO200.DLL" (ByVal Handle%)

Call Syntax:

Location& = Floc&(Handle%)

Where:

Handle% is the DOS file handle that was assigned when the file was opened, and Location is assigned to

the current DOS file pointer location. If the handle is invalid Location is instead assigned a value of -1.

Comments:

Unlike Visual Basic's Seek and Loc, FLoc considers the first byte in a file to be byte 0, not 1. Therefore,

FLoc will return a location of 0 if the current file pointer is at the beginning of the file.

Besides returning a location of -1 if the handle given is invalid, the DOSError and WhichError functions

may also be examined for the error information.

See Also:

See the companion program FSeek which will set the file pointer location.


FLof

assembler function contained in QPRO200.DLL

Purpose:

FLof returns the length of a file that has been opened with the QuickPak Professional for Windows FOpen subroutine. It is similar to the FileSize&() function, except FLof accepts a file handle rather than a name. FLof serves the same purpose as the Visual Basic Lof() function.

Declare Syntax:

Declare Function FLof& Lib "QPRO200.DLL" (ByVal Handle%)

Call Syntax:

Length& = FLof&(Handle%)

Where:

Handle% is the DOS file handle that was assigned when the file was opened, and Length is assigned to the file's length in bytes. If the handle is invalid Length is instead assigned a value of -1.

Comments:

Besides returning a length of -1 if the handle given is invalid, the DOSError and WhichError functions may also be examined for the error information.

See Also:

See the FileSize function which returns a file's size, but it accepts a file name rather than a handle.

Example Location:

Project File 	Form/BASIC File   	Procedure

FILEMOD.MAK	FCOMP.BAS	FileComp


FloppyType

assembler subroutine contained in QPRO200.DLL

Purpose:

Subroutine that reads CMOS memory and returns type of floppy disks installed on system.

Declare Syntax:

Declare Sub FloppyType Lib "QPRO200.DLL" (DriveA%, DriveB%)

Call Syntax:

FloppyType DriveA%, DriveB%

Where:

DriveA/B returns 360,720,1200,1440 respectively.

Comments:

This routine can be used to determine to type of floppy disks that are installed on the host computer.

You could for example, use this information to decide how to format a disk.  To see how much data a

particular disk in a drive can hold, you should instead use the DiskRoom&() function, as some drives are

capable of reading and writing to disks of different capacities.


FOpen and FOpenS

assembler subroutines contained in QPRO200.DLL

Purpose:

FOpen is used to open a disk file in preparation for reading or writing using the QuickPak Professional for Windows file access routines. FOpenS is identical, except it opens a file for Shared (network) access.

Declare Syntax:

Declare Sub FOpen Lib "QPRO200.DLL" (ByVal FileName$, Handle%)

Declare Sub FOpenS Lib "QPRO200.DLL" (ByVal FileName$, Handle%)

Call Syntax:

FOpen FileName$, Handle%

 or

FOpenS FileName$, Handle%

Where:

FileName$ is the name of the file to be opened, and Handle% is assigned by DOS for all subsequent references to the file. If the file does not exist or any other error occurs, Handle% will be returned set to -1.

Comments:

FOpen and FOpenS will open any file, and they also accept an optional drive or directory as part of the file name. However, they will not create a file. If you are not sure if a file exists you should first use the Exist function, or call FCreate to create it.It is up to your program to remember the handle number that DOS assigns, and use that number whenever you access the file again later.

Besides returning a handle of -1 if the named file is not present, the DOSError and WhichError functions

may also be examined for this information.

See Also:

See the section entitled "Eliminating On Error" for more information on DOS handles.

Example Location:

Project File 	Form/BASIC 	File Procedure

FILEMOD.MAK	FCOMP.BAS	FileComp

SEQFILE.MAK	SEQFILE.FRM	Command1_Click


FOpenAll

assembler subroutine contained in QPRO200.DLL

Purpose:

FOpenAll will open a file for any access mode, including all of the variations needed for network

operation.

Declare Syntax:

Declare Sub FOpenAll Lib "QPRO200.DLL" (ByVal FileName$, ByVal  AccessMode%, ByVal _

  Share%, Handle%)

Call Syntax:

FOpenAll FileName$, AccessMode%, Share%, Handle%

Where:

FileName$ holds the file name to open.

AccessMode% 	0       Open file for reading only

1       Open file for writing only

2       Open file for reading and writing

ShareMode%     	0       Deny sharing access (compatibility mode)

1       Deny read/write access

2       Deny write access

3       Deny read access

4       Deny none (full share mode)

Handle% returns with the handle number that DOS assigns. If the file cannot be opened, Handle% will be set to -1.

Comments:

Unlike the QuickPak Professional for Windows FOpen routine which opens a file for read/write access only, FOpenAll provides you with complete control over all of the possible DOS open parameters. These are shown in the table above.

See Also:

See the description for the DOSError and WhichError functions.

Example Location:

Project File 	Form/BASIC File 	Procedure

NETWORK.MAK	NETWORK.FRM	OpenPB


FPut

assembler subroutine contained in QPRO200.DLL

Purpose:

FPut writes data to a disk file in a manner similar to Visual Basic's binary Put command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub FPut Lib "QPRO200.DLL" (ByVal Handle%, Source$)

Call Syntax:

FPut Handle%, Source$

Where:

Handle% is the DOS file handle that was assigned when the file was opened, and Source$ is the string that contains the data to be written.

The length of Source$ determines how many bytes will be written to the file.

Comments:

FPut writes data to the specified file at the location held in the DOS file pointer. The current pointer location is established by the most recent read or write operation, or by using the Visual Basic Seek command or the QuickPak Professional for Windows FSeek subroutine.

Only two errors are likely when using FPut--either the handle number was invalid, or the source string was null. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

You can treat the file as sequential by appending a carriage return/line feed to the end of each string as it

is written:

 CrLf$ = Chr$(13) + Chr$(10)

 FPut Handle%, Source$ + CrLf$

See Also:

FPutT writes file data from information in a Type variable.

Example Location:

Project File 	Form/BASIC File 	Procedure

SEQFILE.MAK	SEQFILE.FRM	Command1_Click


FPutA

assembler subroutine contained in QPRO200.DLL

Purpose:

FPutA is similar to the QuickPak Professional for Windows FPut routine, however it is usually used to

save an array in one operation.

Declare Syntax:

Declare Sub FPutA Lib "QPRO200.DLL" (ByVal Handle%, Array As Any,NumBytes&)

Call Syntax:

FPutA Handle%, Array(Start%), NumBytes&

Where:

Handle% is the DOS file handle that was assigned when the file was opened, and Array(Start) is any array (except a string array) that is to be saved.

NumBytes% indicates the number of bytes to be written.

Comments:

Like its companion program FGetA, FPutA allows your programs to write an entire numeric or Type array to disk in a single operation.

Please note that this subroutine is not applicable arrays of to variants.

See Also:

See the comments that accompany the description for FGetA.

Example Location:

Project File 	Form/BASIC File 	Procedure

GETAPUTA.MAK	GETAPUTA.FRM	Command2_Click


FPutAH

assembler subroutine contained in QPRO200.DLL

Purpose:

FPutAH will write an entire huge array of any size to disk in a single operation.

Declare Syntax:

Declare Sub FPutAH Lib "QPRO200.DLL" (ByVal FileName$, Array As Any,  ByVal ElSize%, ByVal _

  NumEls%)

Call Syntax:

FPutAH FileName$, Array(Start%), ElSize%, NumEls%

Where:

FileName$ is the file to be saved, and Array(Start%) is the first element in the array to be written to the file.  ElSize% indicates the length of each array element in bytes (or a special code to indicate the length),

and NumEls% is the number of elements to write.

Comments:

Unlike the other FPut routines that expect a handle to a file that has already been opened, FPutAH assumes you want to save the entire array at once. This eliminates the extra of steps of having to first open the file, remember the handle, write the data, and finally close the file. FPutAH is used like the QuickPak Professional for Windows QBSave routine, except it is not limited to writing 64K or less.

FPutAH also accepts the same size code that is used by the QuickPak Professional for Windows Type

sort routines:

    -1 = 2-byte integer

    -2 = 4-byte long integer                }these codes are

    -3 = 4-byte single precision         }interchangeable

    -4 = 8-byte double precision

    -5 = 8-byte Currency

If an error occurs while writing a file, the DOSError and WhichError functions will be set appropriately.Please note that this subroutine is not applicable to arrays of variants.

See Also:

See the related routine FGetAH.


FPutR

assembler subroutine contained in QPRO200.DLL

Purpose:

FPutR writes data to a disk file in a manner similar to Visual Basic's random Put command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub FputR Lib "QPRO200.DLL" (ByVal Handle%, Source$, RecNumber&)

Call Syntax:

FputR Handle%, Source$, RecNumber&

Where:

Handle% is the DOS file handle that was assigned when the file was opened, Source$ is the string that holds the data to be written, and RecNumber& is a long integer that indicates the record to be written to.

The length of Source$ determines how many bytes are to be written, and is also used with RecNumber& to determine how far into the file the record is located.

Comments:

Only two errors are likely to occur when using FPutR--either the handle number was invalid, or the source string was null. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

See Also:

See the description for the companion routine FPutRT, which writes the data from a fixed-length string or Type variable.


FPutRT and FPutRTA

assembler subroutines contained in QPRO200.DLL

Purpose:

FPutRT and FPutRTA write data into a disk file in a manner similar to Visual Basic's random Put command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub FPutRT Lib "QPRO200.DLL" (ByVal Handle%, Source As Any,  RecNumber&, ByVal _

  RecSize%)

Declare Sub FputRTA Lib "QPRO200.DLL" (ByVal Handle%, Source As Any,  RecNumber&, ByVal _

  RecSize%)

Call Syntax:

FPutRT Handle%, Source, RecNumber&, RecSize%

FputRTA Handle%, Source(Start), RecNumber&, RecSize%

Where:

Handle% is the DOS file handle that was assigned when the file was opened, and Source is a Type variable that holds the data to be written. RecNumber& is a long integer that indicates the record to be written to, and RecSize% is the length in bytes of each record.

RecSize% determines how many bytes are to be written, and is also used internally by FPutRT along with RecNumber& to determine how far into the file the record is located.

Comments:

FPutRT and FPutRTA are nearly identical, except that FPutRTA is usually used to save one or more elements at once directly from an array. Only two errors are likely to occur when using FPutRT or FPutRTA_either the handle number was invalid, or the record size was given as zero. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.The type of variable being considered as the key is indicated to FPutRT and FPutRTA as follows:

 -1 = integer

 -2 = long integer

 -3 = single precision

 -4 = double precision

 -5 = currency

See Also:

The companion routine FPutR writes data from a conventional string variable.

Example Location (FPutRT):

Project File 	Form/BASIC File 	Procedure

GPREC.MAK	GPREC.FRM	Command3_Click


FPutT

assembler subroutine contained in QPRO200.DLL

Purpose:

FPutT writes data to a disk file in a manner similar to Visual Basic's binary Put command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub FputT Lib "QPRO200.DLL" (ByVal Handle%, ByVal Source As Any, NumBytes&)

Call Syntax:

FPutT Handle%, Source, NumBytes&

Where:

Handle% is the DOS file handle that was assigned when the file was opened, Source is the Type variable that holds the data being written, and NumBytes indicates how many bytes are to be written to the file.

Comments:

FPutT writes data into the specified file at the location held in the DOS file pointer. The current pointer location is established by the most recent read or write operation, or by using the Visual Basic Seek command or the QuickPak Professional for Windows FSeek subroutine.

Only two errors are likely when using FPutT--either the handle number was invalid, or the number of bytes to be written was specified as zero. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

See Also:

The companion routine FPut writes data from a conventional string variable.


FSeek

assembler subroutine contained in QPRO200.DLL

Purpose:

FSeek will position the DOS file pointer for a file that has been opened using the QuickPak Professional for Windows FOpen routine.

Declare Syntax:

Declare Sub FSeek Lib "QPRO200.DLL" (ByVal Handle%, Location&)

Call Syntax:

FSeek Handle%, Location&

Where:

Handle% is the handle that DOS assigned when the file was first opened, and Location& is the location in the file to seek to.

Comments:

Unlike Visual Basic's Seek and Loc, FSeek considers the first byte in a file to be byte 0, not 1. Therefore, to seek to the beginning of a file you would call FSeek with a location value of 0.  The only error that is likely to occur when using FSeek is giving it an invalid handle number. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

One warning you should be aware of is that seeking to a location beyond the end of a file will cause it to be extended. This is not a fault with FSeek, and in fact will happen with Visual Basic's Seek command as well.

To obtain the current seek location for a file, use the QuickPak Professional for Windows FLoc function.


FStamp

assembler subroutine contained in QPRO200.DLL

Purpose:

FStamp creates a new date and time for a specified file.

Declare Syntax:

Declare Sub FStamp Lib "QPRO200.DLL" (ByVal FileName$, ByVal NewTime$, ByVal NewDate$)

Call Syntax:

FStamp FileName$, NewTime$, NewDate$

Where:

FileName$ is the name of the file to process, NewTime$ is a string representing the new time, and NewDate$ is a string holding the new date.

Comments:

FStamp is very capable in that all of the foreign date and time standards are supported, and the format of the new date and time strings is quite flexible.

To apply a new time and leave the current date alone, only the NewTime$ variable should be assigned and NewDate$ will be a null string. Likewise, if only a new date is required, then NewTime$ would be left null. The current system date or time may also be specified by placing an asterisk (*) in either of the strings.

The date and time values must have a valid delimiter between the various pieces that comprise them. For example, a valid date would be any of the following:

   "05/02/88" or "05-02-1988" or "5.2.88" or "5-2-1988"

Regardless of the delimiter used, FStamp will examine the current country information from DOS, in order to determine which format is being used. The three methods recognized are American MM-DD-YY, European DD.MM.YY, or Japanese YY.MM.DD.

Valid delimiters for the time field are either a colon or a period. If only the hour is given, then the minutes and seconds will be set to zero:

   NewTime$ = "5"

Similarly, if only the hour and minutes are given, then the seconds are assumed to be zero:

   NewTime$ = "5:05"

The QuickPak Professional for Windows DOSError% and WhichError% functions should be examined to see if an error occurred during the file stamping operation.

Example Location:

Project File 	Form/BASIC File 	Procedure

FILEMOD.MAK	FILEMOD.FRM	FStampB_Click


GetAttr

assembler function contained in QPRO200.DLL

Purpose:

GetAttr examines a disk file and reports the setting of its DOS attribute byte.

Declare Syntax:

Declare Function GetAttr% Lib "QPRO200.DLL" (ByVal FileName$)

Call Syntax:

Attribute% = GetAttr%(FileName$)

Where:

FileName$ is the file being examined, and Attribute is assigned bit coded with the file's attributes. If the file does not exist or any other error occurs, the attribute variable will be set to -1.

Comments:

Every file has an attribute that is assigned at the time it is created. The attribute information is kept in a disk's directory, along with each file's name, date, and time. There are six possible attributes a file can possess, and in many cases it can have more than one. An assembler program can establish any attribute when it asks DOS to create a file, however Visual Basic offers much less flexibility.

The most common file attribute is Archive, and this simply indicates if the file has been backed up since the last time it was modified. If the archive portion of the attribute byte is set to 1 (true), then the file has been modified but not backed up. DOS sets the archive bit every time a file is written to, and most backup programs clear it as they process each file. Files that are created by Visual Basic have the archive attribute only.

The next attribute is Subdirectory, and this indicates that a file is a DOS subdirectory, as opposed to a program or data file. There is no real difference between a subdirectory and any other file, other than the setting of this bit in its attribute byte.

The third type of attribute is Volume Label. Of course a volume label isn't actually a file, but rather a name that may be given to a diskette or hard disk. However, a disk's volume label is kept in the root directory along with the other file entries.

The final three attributes are System, Hidden, and Read- Only. The most common system files are IBMBIO.COM and IBMDOS.COM, though any file could be defined as a system file. Hidden files are not displayed by the Dir or Files commands, and a file that has been marked as read-only may not be altered. Setting a file to read-only is a good way to prevent it from being accidentally overwritten or erased.

The attribute returned by GetAttr is in the form of a single byte, and the placement of the various bits is

illustrated in the table above.

To test an individual bit, you can use code such as the following:

   Status = Attribute And 2^Bit

Besides returning an attribute of -1 if the named file is not present, the DOSError and WhichError functions may also be examined for error information.

Also provided with QuickPak Professional for Windows is a companion program SetAttr that allows setting a file's attribute.


GetDir

assembler function contained in QPRO200.DLL

Purpose:

GetDir will return the current directory for either a specified drive or the default drive.

Declare Syntax:

Declare Function GetDir$ Lib "QPRO200.DLL" (ByVal Drive$)

Call Syntax:

Directory$ = GetDir$(Drive$)

Where:

Drive$ is a valid drive letter or a null string to indicate the default drive, and Directory$ is assigned the name of the current directory.

Comments:

The Drive$ parameter may be given as either upper or lower case, and only the first character is considered by GetDir.

GetDir returns the complete directory name, minus the drive letter and colon. However, GetDir does not report if the drive was invalid. This may be tested after using GetDir by examining the QuickPak Professional for Windows DOSError and WhichError functions.

Example Location:

Project File 	Form/BASIC File 	Procedure

READDIRS.MAK	READDIRS.FRM	ReadDirs


GetDrive

assembler function contained in QPRO200.DLL

Purpose:

GetDrive returns the current default disk drive.

Declare Syntax:

Declare Function GetDrive% Lib "QPRO200.DLL" ()

Call Syntax:

Drive% = GetDrive%()

or

Drive$ = Chr$(GetDrive%())

Where:

Drive$ is assigned to a value representing an ASCII character that holds the current default drive. GetDrive actually returns an integer result, which can be easily turned into a string as shown above.

Comments:

Because GetDrive has been designed as a function, it must be declared before it may be used.GetDrive returns a value that represents an upper case ASCII drive letter. For example, if the current default drive is A, then GetDrive will return the value 65. Likewise, if the current drive is C, GetDrive would return 67.

Example Location:

Project File 	Form/BASIC File 	Procedure

DISKINFO.MAK	DISKINFO.FRM	Form_Load


GetVol

assembler function contained in QPRO200.DLL

Purpose:

GetVol obtains the disk volume label for either a specified drive or the current default drive.

Declare Syntax:

Declare Function GetVol$ Lib "QPRO200.DLL" (ByVal Drive$)

Call Syntax:

Volume$ = GetVol$(Drive$)

Where:

Drive$ is either an upper or lower case letter of the drive being examined, or a null string to indicate the current default drive. Volume$ is then assigned the disk's volume label.

Comments:

Some programmers like to use a disk's volume label as a way to keep track of its contents. One example might be when writing a major accounting program that must also work with a floppy disk system. You could assign labels for each disk using names such as "CUSTOMER" and "SALES". Then if the operator inadvertently put the wrong disk in the drive, your program would know that and prompt for the correct one.

See Also:

The companion program PutVol will create or modify a disk volume label.

Example Location:

Project File 	Form/BASIC File 	Procedure

DISKINFO.MAK	DISKINFO.FRM	Command1_Click


GoodDrive

assembler function contained in QPRO200.DLL

Purpose:

GoodDrive will quickly determine whether a specified drive letter is valid.

Declare Syntax:

Declare Function GoodDrive% Lib "QPRO200.DLL" (ByVal Drive$)

Call Syntax:

Okay% = GoodDrive%(Drive$)

Where:

Drive$ is either an upper or lower case letter that represents the drive to check, and Okay receives either

-1 if the drive is valid, or 0 if it is not.

Comments:

GoodDrive is provided as a companion for the QuickPak Professional for Windows LastDrive function. Where LastDrive will return the last consecutively available drive in a system, GoodDrive checks a single drive letter for validity.

GoodDrive is needed in those cases where the DOS SUBST command has been used to create an alias drive letter. For example, if a system has physical drives A, B, and C, and a RAM disk or SUBST disk drive G, LastDrive would report drive C as the last available drive.

If a user then asks your program to save some data to drive G, you could call GoodDrive to see if G

really is valid before displaying a warning message.

Example Location:

Project File 	Form/BASIC File 	Procedure

DISKINFO.MAK	DISKINFO.FRM	Command3_Click


InterruptX

assembler function contained in QPRO200.DLL

Purpose:

InterruptX allows you to call DOS Interrupts from Visual Basic.

Declare Syntax:

Declare Function InterruptX% Lib "QPRO200.DLL" (Registers as RegTypeX, ByVal IntNum as Integer)

Call Syntax:

TYPE RegTypeX                   'type structure declared in Global

 AX    AS INTEGER

 BX    AS INTEGER

 CX    AS INTEGER

 DX    AS INTEGER

 BP    AS INTEGER

 SI    AS INTEGER

 DI    AS INTEGER

 Flags AS INTEGER

 DS    AS INTEGER

 ES    AS INTEGER

 END TYPE

Dim Registers as RegTypeX

'fill in registers.xx and IntNum%

If InterruptX% (Registers, IntNum%) then

     Print "Error Occurred"

End If

Where:

Registers is a TYPE RegTypeX variable that contains values for every CPU register (except SS:SP).  IntNum% is the interrupt that you want to call.  If the registers DS, ES and BP are not used by the interrupt or service called, then they should be set to -1.

Comments:

InterruptX can be declared as a function or sub.  If the service returns an error by using the Carry Flag, then the result of the function is a -1.  Otherwise a 0 is returned.  If the service does not return an error, the routine may be declared and called as a subroutine.  In either case, the Flags register will reflect any flag settings as a result.  Not all interrupts or interrupt services are supported by Windows.  Remember that InterruptX only provides the vehicle for calling DOS interrupts and does not check for illegal (unsupported) services.


KillDir

assembler subroutine contained in QPRO200.DLL

Purpose:

KillDir will remove a specified directory like Visual Basic's RmDir command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub KillDir Lib "QPRO200.DLL" (ByVal DirName$)

Call Syntax:

KillDir DirName$

Where:

DirName$ is the directory to be deleted, and may include an optional drive letter or path.

Comments:

The most common errors that are likely to happen when using KillDir would be caused by asking it to remove a directory that doesn't exist, or specifying a directory that contains files. Two other possibilities would be giving an invalid drive or parent directory, or using a wild card as part of the directory name. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

The DCount function may be used to determine the presence of any directory. Even though DCount is meant to return the number of directory names that match a given specification, it will also accept a complete directory name. Thus, if the directory exists, DCount will return 1. And if it doesn't, DCount would instead return 0.

Specifying a directory name can be tricky if it isn't located under a disk's root directory. For example, to remove the directory LEVEL2 which is under the directory \LEVEL1 you could use:

    KillDir "\LEVEL1\LEVEL2"

No matter what directory is current, the correct one will be removed. But if you're already in the \LEVEL1 directory, you might instead use:

     KillDir "LEVEL2"

Notice that the leading back slash is not specified in this example. If it were, DOS would look for a directory named LEVEL2 under the root directory. Of course, LEVEL2 is really under \LEVEL1, so KillDir would return an error.

Also notice that directory names can have an extension, even though most people don't bother with extensions when creating a directory.


KillFile

assembler subroutine contained in QPRO200.DLL

Purpose:

KillFile will delete a specified file like Visual Basic's Kill command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub KillFile Lib "QPRO200.DLL" (ByVal FileName$)

Call Syntax:

KillFile FileName$

Where:

FileName$ is the file to be deleted, and may include an optional drive or path but not wild cards.

Comments:

The most common error that is likely to happen when using KillFile would be caused by asking it to delete a file that isn't there. Two other possibilities would be specifying an invalid drive or directory, or if the file has a Read-Only attribute. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

The Exist function may be used to determine the presence of file. Also, a complete discussion of file attributes is given in the section that describes the GetAttr function.


LastDrive

assembler function contained in QPRO200.DLL

Purpose:

LastDrive will report the last consecutively available drive in a PC.

Declare Syntax:

Declare Function LastDrive% Lib "QPRO200.DLL" ()

Call Syntax:

Last% = LastDrive%()

or

Last$ = Chr$(LastDrive$())

Where:

Last receives the ASCII value of an upper case letter that represents the last consecutively available drive.

Comments:

LastDrive returns the last consecutive drive letter that is present, but it does not always take into account "alias" drives that may also be valid.

For example, a system may have physical drives labeled A through C, as well as a RAM disk set up as drive D. In that case, LastDrive will return 68, which is the ASCII for the letter "D". However, if the DOS SUBST command is also being used to assign a phantom drive "H" to a DOS subdirectory, LastDrive will not detect that.

Adding the code to scan through all of the possible drives is not difficult. However, that would mislead your program into believing that drives E, F, and G were also available when they are not.If you suspect that there may be valid drives beyond the one reported by LastDrive, you should use the GoodDrive function to tell if it really is present. GoodDrive will accept any drive letter, and return -1 if the drive is actually valid.

Example Location:

Project File 	Form/BASIC File 	Procedure

DISKINFO.MAK	DISKINFO.FRM 	Form_Load


LineCount

assembler function contained in QPRO200.DLL

Purpose:

LineCount will quickly count all of the lines of text in a specified file.

Declare Syntax:

Declare Function LineCount& Lib "QPRO200.DLL" (ByVal FileName$, ByVal Buffer$)

Call Syntax:

Count& = LineCount&(FileName$, Buffer$)

Where:

FileName$ is the name of the file to examine, and may contain an optional drive letter or path name. Buffer$ is a temporary work area needed by LineCount to hold the file, and Count receives the number of lines of text.

If the file doesn't exist or some other error occurs, Count will instead receive -1.

Comments:

Besides returning a count of -1 if the file is not present, the QuickPak Professional for Windows DOSError and WhichError functions may be examined for error information.

The main purpose of LineCount is to report the number of lines in a text file, in preparation for dimensioning an array to hold them. When a program will be reading a text file into an array, knowing this ahead of time can be a big help. The only other possibilities would be to read through the entire file once just to count the lines, or guess at how large to dimension the array to.

Visual Basic's Input command on sequential files is painfully slow, because every character must be examined looking for a Chr$(13) that marks the end of a line, or a Chr$(26) that marks the end of the file.

Further, numbers stored in a sequential file must be converted to the internal format used by Visual Basic which also takes time. This clearly rules out reading the file an extra time just to count the lines.  Guessing at how large to dimension an array is an equally poor practice. If you guess too small, you'll either crash completely, or at best have to re-dimension the array and start all over again. And if you guess too high, you've wasted four bytes of string memory for each extra element that was created.  One important caveat you should be aware of when using LineCount is that it merely counts the number of Chr$(13) carriage returns in the file. (Though it does so much faster than Visual Basic ever could.) Line feeds are not considered at all.

Another important point is that your program must provide a data buffer to hold the file's contents as it is examined. This will be supplied in the form of a conventional or fixed-length string, and it must be at least 256 characters long. The minimum recommended buffer size is 512 bytes (the size of a DOS sector), but a string length of 4096 bytes would be ideal.

Rather than assign a string prior to calling LineCount, the best approach would be to have Visual Basic create it on the fly as shown below:

    Count = LineCount%(FileName$, Space$(4096))

When Space$ (or String$) is used as an argument to a subroutine, the memory it occupies is released back to the program as soon as the subroutine finishes.


LockFile

assembler subroutine contained in QPRO200.DLL

Purpose:

LockFile will lock all or a portion of a network file, but without needing On Error.

Declare Syntax:

Declare Sub LockFile Lib "QPRO200.DLL" (ByVal Handle%, Offset&, Length&)

Call Syntax:

LockFile Handle%, Offset&, Length&

Where:

Handle% is the handle assigned by DOS when the file was opened, Offset& is the starting offset into the file where locking is to begin, and Length& is the number of bytes to lock.

Comments:

Only two errors are likely when using LockFile, 73 and 85, and these may be determined with the QuickPak Professional for Windows DOSError and WhichError functions.

To use LockFile you must provide a file handle, as well as the range of bytes to lock. These are easily determined based on the record number being locked, and the length of each record. File offsets are zero-based, which means that the first byte in the file is zero, not one. If the file is successfully locked, it will not be necessary to do so again in Visual Basic. Also, portions of a file must be unlocked in the exact same manner in which they were locked. If you lock, say, bytes 100 through 200, then you must unlock the identical range later.

Understand that when an application locks all or part of a file, the locking applies only to other programs. Although other programs are restricted from both reading and writing to that portion of the file, your application still has complete access to the entire file.


MakeDir

assembler subroutine contained in QPRO200.DLL

Purpose:

MakeDir will create a directory in the same way that Visual Basic's MkDir will, but without requiring the need for On Error.

Declare Syntax:

Declare Sub MakeDir Lib "QPRO200.DLL" (ByVal DirName$)

Call Syntax:

MakeDir DirName$

Where:

DirName$ is the name of the directory to create, and may also include an optional drive letter or path.

Comments:

The only errors that are likely to happen when using MakeDir would be caused by specifying an invalid drive letter, or a parent directory that does not exist. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

Specifying a directory name can be tricky if it isn't located under a disk's root directory. For example, to create the directory LEVEL2 under the directory \LEVEL1 you could use:

   MakeDir "\LEVEL1\LEVEL2"

No matter what directory is current, the correct one will be created. But if you're already in the \LEVEL1 directory, you might instead use:

   MakeDir "LEVEL2"

Notice that the leading back slash is not specified in this example. If it were, DOS would create a directory named LEVEL2 under the root directory, which of course is not what was intended.


NameDir

assembler subroutine contained in QPRO200.DLL

Purpose:

NameDir will rename a directory.

Declare Syntax:

Declare Sub NameDir Lib "QPRO200.DLL" (ByVal OldName$, ByVal NewName$)

Call Syntax:

NameDir OldName$, NewName$

Where:

OldName$ is the original directory name, and NewName$ is the name it is to become.

Comments:

The only errors that are likely to occur when using NameDir would be specifying OldName$ for a directory that doesn't exist, or giving a NewName$ for a directory that does. One other possibility would be using a wild card in either name.

Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError

functions.

The DCount function may be used to determine the presence of a directory.It is important to understand that renaming a directory from within a running program can be dangerous. For example, if you allow a user to rename directories, you may not be able to find a data file afterward. Also, if a directory that is one or more levels above the current one is changed, then the current directory's name will be changed as well.

You must provide the full path to both the old and the new name.  For example, if OldName$ is "C:\VB\THIS" then NewName$ should be "C:\VB\THAT" not "THAT".


NameFile

assembler subroutine contained in QPRO200.DLL

Purpose:

NameFile will rename a file in the same way that Visual Basic's Name As command will, but without requiring On Error.

Declare Syntax:

Declare Sub NameFile Lib "QPRO200.DLL" (ByVal OldName$, ByVal NewName$)

Call Syntax:

NameFile OldName$, NewName$

Where:

OldName$ is the name the file currently has, and NewName$ is the name it is to become.

Comments:

The only errors that are likely to occur when using NameFile would be specifying OldName$ for a file that doesn't exist, or giving a NewName$ for a file that does. Two other possibilities would be using a wild card in either name, or if the file has a Read-Only attribute. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

The Exist function may be used to determine the presence of a file. Also, a complete discussion of the Read-Only and other file attributes is given in the section that describes the GetAttr function.

Example Location:

Project File 	Form/BASIC File 	Procedure

FILEMOD.MAK	FILEMOD.FRM	NameFileB_Click


NetDrive

assembler function contained in QPRO200.DLL

Purpose:

NetDrive reports if a given drive is remote (on a network).

Declare Syntax:

Declare Function NetDrive% Lib "QPRO200.DLL" (ByVal Drive$)

Call Syntax:

Remote% = NetDrive%(Drive$)

Where:

Drive$ is either an upper or lower case letter that represents the drive to check, and Remote% receives either -1 if the drive is on a network, or 0 if it is a local drive.

Comments:

Drive$ may be upper or lower case, and only the first character is considered.  If Drive$ is null, the current default drive is tested.

Also See:

The Removable function tests for a floppy drive.


PutVol

assembler subroutine contained in QPRO200.DLL

Purpose:

PutVol will create a disk volume label for either a specified drive or the current default drive. If a label already exists, PutVol will instead rename it.

Declare Syntax:

Declare Sub PutVol Lib "QPRO200.DLL" (ByVal Drive$, ByVal Label$)

Call Syntax:

PutVol Drive$, Label$

Where:

Drive$ is either an upper or lower case letter for the drive being examined, or a null string to indicate the current default drive. Label$ is then written to the disk's directory as a volume label.

Comments:

Some programmers like to use a disk's volume label as a way to keep track of its contents. One example might be when writing a major accounting program that must also work with a floppy disk system. You could then label the customer file disk, say, "CUSTOMER", and the inventory disk "INVENTORY". If a user inadvertently puts the wrong disk in the drive, your program could know that and prompt for the correct one.

A disk volume label may be up to eleven characters in length.

See Also:

The companion program GetVol will obtain the volume label from a disk.

Example Location:

Project File 	Form/BASIC File	Procedure

DISKINFO.MAK	DISKINFO.FRM	Command2_Click


QBLoad

assembler subroutine contained in QPRO200.DLL

Purpose:

QBLoad serves the same purpose as DOS BASIC's BLOAD command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub QBLoad Lib "QPRO200.DLL" (ByVal FileName$, Array%)

Call Syntax:

QBLoad FileName$, Array%(Element%)

Where:

FileName$ is the name of a file that had been previously saved using either QBSave or DOS BASIC's BSAVE.

To load the file into Array(Element) specify the array element as shown.

Comments:

The only errors that are likely to be encountered with QBLoad would be specifying a file that doesn't exist, or giving an invalid drive letter or path. Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.

Two important uses for QBLoad and DOS BASIC's BLOAD are saving and loading graphics screen images, or reducing the time needed to load a numeric array.

Please note that this subroutine is not applicable to array of variants.


QBSave

assembler subroutine contained in QPRO200.DLL

Purpose:

QBSave serves the same purpose as DOS BASIC's BSAVE command, but it returns an error code rather than requiring the use of On Error.

Declare Syntax:

Declare Sub QBSave Lib "QPRO200.DLL" (ByVal FileName$, Array%, NumBytes&)

Call Syntax:

QBSave FileName$, Array%(Element%), NumBytes&

Where:

FileName$ is the name of the file, and NumBytes& indicates how many bytes to save. To save a file from Array%(Element%) specify the array element as shown.

Comments:

The only errors that are likely to occur when using QBSave is giving it an illegal file name, specifying an invalid drive letter or path, or not having sufficient room on the disk.

Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError

functions.


ReadDir

assembler subroutine contained in QPRO200.DLL

Purpose:

ReadDir obtains a list of directory names from disk, and loads them into a conventional (not fixed-length) string array in one operation.

Declare Syntax:

Declare Sub ReadDir Lib "QPRO200.DLL" (DirArray$)

Call Syntax:

ReadDir DirArray$(0)

Where:

DirArray$(0) holds the search specification to indicate which directory names are to be loaded, and subsequent array elements receive each directory name.

Comments:

It is essential that enough elements have been set aside in the string array to hold all of the anticipated directory names. All of the steps needed to obtain a list of directory names are outlined below.Before the array can be dimensioned to hold the directory names, you will need some way to determine how many there are. The easiest way to do this is with the DCount function. DCount accepts the same type of search specification you will give to ReadDir, and it tells how many matching directories there are.  The next step is to dimension the array to the number of elements DCount returned.

Most people think of the DOS wild cards (? and *) as being applicable only to file names, however they are also intended to be used with directory names. For example, to read a list of directory names that begin with the letter "A" and are located under the root directory, you would specify a search specification of "\A*.*" when using DCount and ReadDir.

The program fragment below shows how to obtain a list of all directories under the current directory of drive C.

Spec$ = "C:\*.*"  	'*.* matches any directory  name

Count = DCount%(Spec$) 	'see how many there are

Dim Array$(0 To Count) 	'create the string array

Array$(0) = Spec$  	'put the spec in element 0

ReadDir Array (0)  	'call ReadDir

For X = 1 To Count  	'print them to show it worked

  Print Array$(X)

Next

A complete directory searching program is provided in the file named READDIRS.BAS. ReadDirs will search through all of the directories on a disk_no matter how deeply nested_and display all of the files that match a given specification. ReadDirs is similar to the various "WHEREIS" programs available in the public domain, and it illustrates how recursion can be used to advantage in such a situation.

See Also:

The related routine ReadDirT that reads a list of directory names into a fixed-length string or Type array.

Example Location:

Project File 	Form/BASIC File 	Procedure

READDIR.MAK	READDIR.FRM	Command1_Click


ReadDirs

Visual Basic example program contained in READDIRS.FRM

Purpose:

Unlike most of the QuickPak Professional for Windows subroutines that are called by your Visual Basic programs, ReadDirs is intended to serve as an example for calling the various DOS services. It is also a very handy utility program.

ReadDirs first asks for a file specification such as "C:*.*" or "*.BAS", and then searches all of the directory levels on the disk for files that match. When running ReadDirs, do not give it a directory name

for the files, because all of the disk's directories will be searched automatically.

ReadDirs brings together a number of important DOS routines that are provided with this package, and provides examples for using them in context. It also illustrates how recursion can be used to advantage when navigating tree structured data such as a disk's directory.

Example Location:

Project File 	Form/BASIC File 	Procedure

READDIRS.MAK	READDIRS.FRM	ReadDirs


ReadDirT

assembler subroutine contained in QPRO200.DLL

Purpose:

ReadDirT obtains a list of directory names from disk, and loads them into a Type array.

Declare Syntax:

Declare Sub ReadDirT Lib "QPRO200.DLL" (ByVal Spec$, Array As FILE_NAME)

Call Syntax:

ReadDirT Spec$, Array(1)

Where:

Array(1) has been dimensioned as a Type array, and Spec$ holds the search specification to tell which directory names are to be loaded. Subsequent array elements then receive each directory name.

Comments:

It is essential that enough elements have been set aside in the string array to hold all of the anticipated directory names.

An example of reading directory names into a Type array follows.

Type FILE_NAME

  S As String * 12

End Type

Spec$ = "C:*.*"

Count = DCount%(Spec$)

Dim Array(1 To Count) As FLen

ReadDirT Spec$, Array(1)

For X = 1 To Count

   Print Array(X).S

Next


ReadFile

assembler subroutine contained in QPRO200.DLL

Purpose:

ReadFile obtains a list of file names from disk, and loads them into a conventional (not fixed-length) string array in one operation.

Declare Syntax:

Declare Sub ReadFile Lib "QPRO200.DLL" (Array$)

Call Syntax:

ReadFile Array$(0)

Where:

Array$(0) holds the search specification to indicate which file names are to be loaded, and subsequent array elements receive each name.

Comments:

It is essential that enough elements have been set aside in the string array to hold all of the anticipated file names. All of the steps needed to obtain a list of file names are outlined below.

Before the array can be dimensioned to hold the names, you will need some way to determine how many there are. The easiest way to do this is with the FCount function. FCount accepts the same type of search specification you will give to ReadFile, and it tells how many matching names there are.  The next step is to dimension the array to the number of elements FCount returned.

The DOS wild cards will most likely be used in the search specification, and an optional drive letter or path may also be included. For example, to read a list of file names that begin with the letter "A" and are located under the root directory, you would specify a search specification of "\A*.*" when using FCount and ReadFile. To obtain a list of all Visual Basic program files in the current directory you would instead use "*.BAS".

The example program below shows how to obtain a list of all the file names under the current directory of

drive C.

Spec$ = "C:\*.*" 	'*.* matches any file name

Count = FCount%(Spec$) 	'see how many there are

Dim Array$(0 To Count) 	'create the string array

Array$(0) = Spec$ 	'put the spec in element 0

ReadFile Array$(0) 	'call ReadFile

For X = 1 To Count 	'print them to show it worked

   Print Array$(X)

Next

A complete directory searching program is provided in the file named READDIRS.FRM. ReadDirs will search through all of the directories on a disk--no matter how deeply nested--and display all of the files that match a given specification. ReadDirs is similar to the various "WHEREIS" programs available in the public domain, and it illustrates how recursion can be used to advantage in such a situation.

See Also:

ReadFileT loads a group of file names into a fixed-length string or Type array.

Example Location:

Project File 	Form/BASIC File	Procedure

FILEMOD.MAK	FILEMOD.FRM	Command1_Click


ReadFileI

assembler subroutine contained in QPRO200.DLL

Purpose:

ReadFileI obtains a list of file names, sizes, dates, and times from disk, and then formats and loads them into a conventional (not fixed-length) string array in one operation.

Declare Syntax:

Declare Sub ReadFileI Lib "QPRO200.DLL" (Array$)

Call Syntax:

ReadFileI Array$(0)

Where:

Array$(0) holds the search specification to indicate which file names are to be retrieved, and subsequent array elements receive each file's name and related information.

Comments:

ReadFileI (Read File names with full Information) is similar to the QuickPak Professional for Windows ReadFile routine, except it also obtains the size, date, and time for each file. Before ReadFileI, the only way to do this was to call the ReadFile routine, and then call FileInfo repeatedly for each file. Besides greatly simplifying the amount of work you must do to obtain this information, ReadFileI is also considerably faster.

Because this routine creates Visual Basic strings, it is up to you to assign each string to a length of 37 characters before calling ReadFileI. The example below shows all of the steps needed to set up and call ReadFileI.

Spec$ = "*.*"   	'or whatever

Count = FCount%(Spec$) 	'see how many files there are

Dim Array$(Count)  	'make an array to hold the files

For X = 1 To Count  	'fill each with blanks

  Array$(X) = Space$(37) 	'37 are needed for the information

Next

Array$(0) = Spec$  	'assign search spec to lowest element

ReadFileI Array$(0)  	'call ReadFileI

For X = 1 To Count  	'print the files to prove it worked

  Print Array$(X)

Next

The information returned in each element of the array is organized as shown in the table below. The name can be up to twelve characters, the size could hold as many as eight digits, the date is always eight digits, and the time is always six. A single blank space is placed between each field area in the string.

  1234567890123456789012345678901234567

  namename.ext sizesize mm-dd-yy hh:mm* <<-either "a" or "p"

See Also:

The comments that accompany the ReadFile routine contain more information about the search specification and the FCount function.

Example Location:

Project File 	Form/BASIC File 	Procedure

RFILES.MAK	RFILES.FRM	Command3_Click


ReadFileT

assembler subroutine contained in QPRO200.DLL

Purpose:

ReadFileT obtains a list of file names from disk, and loads them into a Type array in one operation.

Declare Syntax:

Declare Sub ReadFileT Lib "QPRO200.DLL" (ByVal Spec$, Array As FILE_NAME)

Call Syntax:

ReadFileT Spec$, Array(1)

Where:

Array(1) has been dimensioned as a Type array, and Spec$ holds the search specification to tell which file names are to be loaded. Subsequent array elements then receive each name.

Comments:

It is essential that enough elements have been set aside in the Type array to hold all of the anticipated file names.  Further, the size of each element must be exactly twelve, to reserve room for each name.

The steps needed to read file names into a Type string array are similar to those for a conventional string array, as outlined in the example for ReadFile. However, there are some differences, and a modification of the code shown in the ReadFile example below.

Type FILE_NAME

   S As String * 12

End Type

Spec$ = "C:*.*"

Count = FCount%(Spec$)

Dim Array(1 To Count) As FLen

ReadFileT Spec$, Array(1)

For X = 1 To Count

   Print Array(X).S

Next

Please note that this subroutine is not applicable to arrays of variants.

Example Location:

Project File 	Form/BASIC File 	Procedure

RFILES.MAK	RFILES.FRM	Command2_Click


ReadFileX

assembler subroutine contained in QPRO200.DLL

Purpose:

Like ReadFileI, ReadFileX (Read File names Extended) obtains a list of file names, sizes, dates, and times from disk, except it places these into separate components of a Type array.

Declare Syntax:

Declare Sub ReadFileX Lib "QPRO200.DLL" (ByVal Spec$, DirSize&, Array As FULLINFO)

Call Syntax:

ReadFileX Spec$, DirSize&, Array(1)

Where:

Spec$ holds the search specification to indicate which file names are to be retrieved, DirSize& receives the total of all the file sizes, and elements in the array receive each file's name and related information.

Comments:

ReadFileX expects you to pass to it a user-defined Type array that has been dimensioned sufficiently for the number of files that will be read. This array must be structured and dimensioned as follows:

Type FULLINFO   'this is the Type definition

  BaseName As String * 8

  ExtName As String * 3

  FileSize As Long

  FileDate As String * 8

  FileTime As String * 6

  Attrib   As String * 1

End Type

Count = FCount%(Spec$) 	'use FCount to count the files

Dim Array(1 To Count) As FullInfo 	'dimension the array

See Also:

See the comments that accompany the description for ReadFileI

Example Location:

Project File 	Form/BASIC File	Procedure

RFILES.MAK	RFILES.FRM	Command4_Click


ReadSect

assembler subroutine contained in QPRO200.DLL

Purpose:

ReadSect will read the contents of any disk sector into a conventional (not fixed-length) string.

Declare Syntax:

Declare Sub ReadSect Lib "QPRO200.DLL" (ByVal Drive$, Info$, ByVal Sector&)

Call Syntax:

ReadSect Drive$, Info$, Sector&

Where:

Drive$ is an upper or lower case letter specifying the disk drive to read from.  Unlike most of the other QuickPak Professional for Windows DOS routines, a null string may not be used to indicate the default drive.

Info$ is a string already containing at least 512 characters or spaces that receives the sector contents, and Sector& indicates the sector number to read.

Comments:

To accommodate very large hard disks, the sector number is given as a Long Integer.  To specify a long integer constant, simply append a trailing ampersand (&) to the value, as shown below:

    ReadSect Drive$, Info$, 43800&

Errors may be detected with the QuickPak Professional for Windows DOSError and WhichError functions.  Notice that ReadSect is not intended for use with network drives.

Example Location:

Project File 	Form/BASIC File	Procedure

READSECT.MAK	READSECT.FRM	ReadSector


ReadTest

assembler function contained in QPRO200.DLL

Purpose:

ReadTest will report whether a specified disk drive is ready for reading.

Declare Syntax:

Declare Function ReadTest% Lib "QPRO200.DLL" (ByVal Drive$)

Call Syntax:

Okay% = ReadTest% (Drive$)

Where:

Drive$ is either an upper or lower case letter that represents the disk drive to check, or a null string to

indicate the current default drive. Okay is then assigned -1 if the drive is ready, or 0 if it is not.

Comments:

ReadTest does not check to see if a valid drive letter has been specified. It only tests to see that a disk is in the drive and the door is closed. If you also need to determine whether the drive letter is valid, you should first use the GoodDrive function.

Example Location:

Project File 	Form/BASIC File 	Procedure

DISKINFO.MAK	DISKINFO.FRM	Command1_Click


Removable

assembler function contained in QPRO200.DLL

Purpose:

Removable reports if a given drive's media is removable (a floppy drive).

Declare Syntax:

Declare Function Removable% Lib "QPRO200.DLL" (ByVal Drive$)

Call Syntax:

Floppy% = Removable%(Drive$)

Where:

Drive$ is either an upper or lower case letter that represents the drive to check, and Floppy% receives either -1 if the drive media is removable, or 0 if it is anything else.

Comments:

Removable% returns 0 for hard disks, ram drives, and network drives. Drive$ may be upper or lower case, and only the first character is considered.  If Drive$ is null, the current default drive is tested.

Also See:

See the NetDrive function which tests for a network drive.


ScanFile

Visual Basic function contained in SCANFILE.BAS

Purpose:

ScanFile will quickly scan through a specified file looking for a particular text string.

Syntax:

Found = ScanFile&(FileName$, Text$, Start&)

Where:

FileName$ is the file to be examined, Text$ is the text to find, Start& is the starting offset at which to begin searching, and Found receives the offset at which the text was located.  Text$ may contain any number of "?" wild cards, and searching is case insensitive.

If the text is not located, then Found will receive 0.  If a disk error occurs, or an invalid path, drive, or file name is given, Found instead receives -1.

Comments:

ScanFile is written in Visual Basic, however, the actual reading of the file is performed by the QuickPak Professional for Windows binary file access routines.

Even though ScanFile returns a -1 if an error occurs, the QuickPak Professional for Windows DOSError and WhichError functions may also be examined for errors.

ScanFile searches without regard to capitalization, but comments in the source code to SCANFILE.BAS show how this may be changed.  Search for "QInstr2" to find where it is used, and replace that with QInstr.

The position in the file that is returned by ScanFile is based at one.  That is, if a match is found at the very first byte in the file, ScanFile will return 1, and not 0.

The starting offset parameter is used to let you continue a search, or examine only a portion of a file.  For Example, the first time ScanFile is used, Start& should be set to 1.  If a match is found but it is not the correct one, you would invoke ScanFile a second time with the starting offset set to the offset it first returned

Example Location:

Project File 	Form/BASIC File 	Procedure

DEMOSCAN.MAK	DEMOSCAN.FRM	FindIt_Click


SetDrive

assembler subroutine contained in QPRO200.DLL

Purpose:

SetDrive allows changing the current default drive.

Declare Syntax:

Declare Sub SetDrive Lib "QPRO200.DLL" (ByVal Drive$)

Call Syntax:

SetDrive Drive$

Where:

Drive$ is either an upper or lower case letter of the drive to be made the current default.

Comments:

SetDrive is extremely simple to set up and call. Besides using a string variable to specify the new drive letter, you may also give it a string literal:

  SetDrive "A"

or

  SetDrive FileName$

You may use the QuickPak Professional for Windows DOSError and WhichError functions to determine if the new drive was valid.

Example Location:

Project File 	Form/BASIC File	Procedure

READDIRS.MAK	READDIRS.FRM	ReadDirs


SetError

assembler subroutine contained in QPRO200.DLL

Purpose:

SetError allows a Visual Basic program to set or clear the DOSError and WhichError functions.

Declare Syntax:

Declare Sub SetError Lib "QPRO200.DLL" (ByVal ErrCode%)

Call Syntax:

SetError ErrCode%

Where:

ErrCode% is either zero to clear DOSError and WhichError, or an error value to place into WhichError. If ErrCode% is not zero, then DOSError will be set to -1.

Comments:

You probably will not need to set the DOSError and WhichError function values in your programs. However, this capability can be important when you are creating your own DOS services in Visual Basic. For example, the QuickPak Professional for Windows ScanFile function uses SetError to indicate

whether the file was read successfully.

Example Location:

Project File 	Form/BASIC File 	Procedure

FILEMOD.MAK	FCOMP.FRM	FileComp


Unique

Visual Basic function contained in UNIQUE.BAS

Purpose:

Unique will return a file name that does not already exist on the default drive in the current directory.

Syntax:

FileName$ = Unique$(Path$)

Where:

FileName$ receives a unique file name.

Comments:

There are several situations where a program will need to create a temporary file with a unique name.  For example, you might want to pass information to a subsequently run program, or perhaps save the intermediate passes of a multiple pass file sort.

The Path$ argument lets you ensure that the file name returned does not already exist in the specified

directory.


UnLockFile

assembler subroutine contained in QPRO200.DLL

Purpose:

UnLockFile will unlock all or a portion of a network file in the same way that Visual Basic's UnLock will, but without needing On Error.

Declare Syntax:

Declare Sub UnLockFile Lib "QPRO200.DLL" (ByVal Handle%, Offset&, Length&)

Call Syntax:

UnLockFile Handle%, Offset&, Length&

Where:

Handle% is the handle that DOS assigned when the file was first opened, Offset& is the starting offset into the file where the unlocking is to begin, and Length& is the number of bytes to unlock.

Comments:

Only two errors are likely when using UnLockFile, and these may be determined by examining the DOSError and WhichError functions.

To use UnLockFile, you must provide a file handle, as well as the range of bytes in the file. These are easily determined based on the record number to be unlocked, and the length of each record. Long integer values are used because you may also need to unlock a large range of records, or even the entire file. Of course, the values to unlock a large file cannot be represented by a conventional integer variable. Notice that if the file is successfully unlocked, it will not be necessary to do it again in Visual Basic.

See Also:

The comments and warnings that accompany the description for LockFile.


Valid

assembler function contained in QPRO200.DLL

Purpose:

Valid examines a string to see if it could be a valid file name.

Declare Syntax:

Declare Function Valid% Lib "QPRO200.DLL" (ByVal FileName$)

Call Syntax:

Okay% = Valid%(FileName$)

Where:

FileName$ contains the name of a file, and may optionally include a drive letter and colon, as well as a path name. If the name is legal according to the rules of DOS, Okay will receive -1. Otherwise Okay will be set to zero.

Comments:

Valid provides a very quick way to determine if a DOS file name is syntactically correct.  However, it does not check to see if the named file, drive, and path actually exist. Rather, it simply reports if the name could be valid.

Notice that Valid considers blank spaces to be illegal, so you should use LTrim$ and RTrim$ if there is any possibility that the name may contain leading or trailing blanks:

 Okay = Valid%(LTrim$(RTrim$(FileName$)))

Also notice that Valid follows the rules of DOS exactly. That is, a filename may be longer than eight characters, and an extension may be longer than three. In those cases, DOS will truncate the excess.


WhichError

assembler function contained in QPRO200.DLL

Purpose:

WhichError reports which error if any occurred during the last call to a QuickPak Professional for Windows DOS routine.

Declare Syntax:

Declare Function WhichError% Lib "QPRO200.DLL" ()

Call Syntax:

Error% = WhichError%()

Where:

WhichError% returns 0 if there was no error, or an error code if there was.

Comments:

All of the QuickPak Professional for Windows routines assign a value to the DOSError and WhichError functions to indicate their success or failure. Rather than requiring you to set up a separate error handling procedure and use On Error, you can simply query these functions after performing any QuickPak Professional for Windows DOS operation. WhichError is discussed in the section entitled "Eliminating On Error".

The table on the following page shows all of the possible DOS errors and the corresponding numbers

that WhichError returns.

See Also:	DOSError

Example Location:

Project File 	Form/BASIC File 	Procedure

DISKINFO.MAK	DISKINFO.FRM	Command1_Click

QuickPak Professional for Windows Error Codes

Err         	Description                   	Typical Situation

 7	Out of memory      	FastLoadInt

 14          	Out of string space 	FastLoadInt/FastSave

 25         	Device fault               	Disk write error

 27          	Out of paper             	Printer error

 52         	Invalid file handle 	File access

 53          	File not found         	File access

 57          	Device I/O error    	Disk not formatted

 58          	File/Path already exists 	NameFile or NameDir

 61          	Disk is full	File writes, opens, etc.

 62          	Input past end of file     	File reads

 64          	Bad file name	Filename null or too long

 67          	Directory is full            	FCreate, MakeDir

 68          	Device unavailable       	Unknown drive

 70          	Permission denied        	Disk write-protected

 71          	Disk not ready	Drive door open

 72          	Disk media error	Bad disk sector

 73          	Advanced feature not    	LockFile with DOS 2.x available

 74          	Rename across disks     	NameFile

 75          	Access denied	Read-only files

 76          	Path not found              	Invalid drive or path

 77          	Invalid drive spec         	DiskInfo, SetDrive

 78          	Too many handles	FOpen

 79          	Bad FAT image	WriteSect to FAT

 80          	Invalid time data           	FStamp

 81          	Invalid date data           	FStamp

 82          	Invalid parameter          	File handles, etc.

 83          	Buffer too small            	LineCount

 84          	Current directory has    	NameDir been renamed

 85          	Lock conflict	Lock

 86          	Sharing conflict            	FOpenAll

 87          	Read-only conflict         	FCreate

 100         	Insufficient number of

elements	String Restore

 127         	Undefined error


WriteSect and WriteSect2

assembler subroutines contained in QPRO200.DLL

Purpose:

WriteSect will write new contents to any disk sector from either a conventional or fixed-length string.

Declare Syntax:

Declare Sub WriteSect Lib "QPRO200.DLL" (ByVal Drive$, Info$, ByVal Sector&)

Call Syntax:

CALL WriteSect(Drive$, Info$, Sector&)

Where:

Drive$ is an upper or lower case letter specifying the disk drive to write to.  Unlike most of the other QuickPak Professional for Windows DOS routines, a null string may not be used to indicate the default drive.

Info$ is a string that contains the new sector contents to be written (512 bytes), and Sector& indicates the

sector number.

Comments:

WARNING:

Like ReadSect, this is a very powerful routine_in fact, perhaps a bit too powerful!  It will write directly to any disk sector you specify, filling it with the contents of the stated string.

Be warned, WriteSect has the potential to totally devastate a disk.  Please be careful!  If you aren't sure how to use WriteSect, then you have no business fooling around with it.  We have thoroughly tested this routine, however you are on your own.   If you trash your hard disk, forget where you got this program.


WriteTest

assembler function contained in QPRO200.DLL

Purpose:

WriteTest will report whether a specified disk drive is ready for writing.

Declare Syntax:

Declare Function WriteTest% Lib "QPRO200.DLL" (ByVal Drive$)

Call Syntax:

Okay% = WriteTest%(Drive$)

Where:

Drive$ is either an upper or lower case letter that represents the disk drive to check, or a null string to indicate the current default drive. Okay is then assigned -1 if the drive is ready, or 0 if it is not.

Comments:

WriteTest does not check to see if a valid drive letter has been specified. It only tests if a disk is in the drive, is not write-protected, and the drive door is closed. If you also need to determine whether the drive letter is valid, you should first use the GoodDrive function.

WriteTest will also not detect if a disk is merely full. If you need to insure against insufficient disk space,

you should use the DiskRoom function after WriteTest.

Notice that WriteTest creates a temporary file named "LIKE-WOW.MAN" on the disk being tested. In the unlikely event that a file with the same name is already present, it will be overwritten in the process.

Example Location:

Project File 	Form/BASIC File	Procedure

DISKINFO.MAK	DISKINFO.FRM	Command3_Click


Temp Title 85
[Blank Page]


Chp4-TOC

Functions

Bin2Num

C2F

Delimit

Eval

ExpandTab

F2C

LastFirst

LastLast

Num2Bin and Num2Bin2

Pad

Parse

ParseStr

QPHex

ShrinkTab

Signed

UnParseStr

UnSigned

Mathematical Functions

QPACOS

QPASIN

QPATAN2

QPLOG10

QPROUN

Financial Functions

QPFV

QPFVN

QPFVP

QPFVD

QPFVND

QPFVPD

QPPMT

QPPV

QPPVN

QPPMTD

QPPVD

QPPVND

QPCINT

QPCTERM

QPIRR

QPNPV

QPRATE

Depreciation:

QPDDB

QPSLN

QPSYD

Statistical Functions

QPAVG

QPCOUNT

QPMAX

QPMIN

QPSTD

QPSUM

QPVAR


Bin2Num

assembler function contained in QPRO200.DLL

Purpose:

Bin2Num accepts a binary number in the form of a string, and returns an equivalent Integer value.Declare

Declare Syntax:

Declare Function Bin2Num% Lib "QPRO200.DLL" (Binary$)

Call Syntax:

Number% = Bin2Num%(Binary$)

Where:

Binary$ is a string containing only the characters "1" and "0", and Number% receives its value.

Comments:

Bin2Num is designed as an integer function, therefore the values it returns will be considered to be "signed". That is, Visual Basic always considers integer values larger than 32767 as negative numbers.To convert a signed number to its equivalent unsigned value, simply add 65536 as shown below:

Value& = Bin2Num%("1101111011000101")

If Value& <<0 Then Value& = Value& + 65536

See Also:

The related functions Num2Bin and Num2Bin2.


C2F

Visual Basic function contained in FNOTHER.BAS

Purpose:

C2F will convert a centigrade temperature to its Fahrenheit equivalent.

Syntax:

FTemp! = C2F!(CTemp!)

Where:

CTemp! is a valid Centigrade temperature, and FTemp! receives an equivalent in degrees Fahrenheit.

Comments:

Also see the companion function F2C! that converts from Fahrenheit to Centigrade.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOFN.MAK	FNNUM.FRM	C2FPB_Click


Delimit

Visual Basic function contained in FNOTHER.BAS

Purpose:

Delimit counts the number of delimiters in a string, by matching against a second string that contains a table of valid delimiters.

Syntax:

Count = Delimit%(Work$, Table$)

Where:

Work$ is a string such as the current DOS PATH or Command$, Table$ contains a list of acceptable delimiters, and Count receives the total number of matching delimiters found in Work$.

Comments:

Delimit is intended to be used in conjunction with the Parse function, which extracts individual items from a list and places them into an array. Delimit merely counts the number of delimiters in a string, based on a table you provide.For example, to isolate the various components of a DOS PATH (obtained with Visual Basic's Environ$ command), you would use the semi-colon (;) as a delimiter. However, Delimit will also accept a table of possible delimiters, and report how many are contained in the string being examined.If you need to isolate all of the possible Command$ switches a user gave when starting your program, you would probably include several delimiting characters, as shown below.

Count = Delimit%(Command$, "/,-+ ")

Example Location:

Project File	Form/BASIC File	Procedure

DEMOSFN.MAK	FNSDEMO.FRM	DelimitPB_Click


Eval

Visual Basic function contained in FNOTHER.BAS

Purpose:

Eval will return the value of a string like Visual Basic's Val function, but without regard to dollar signs, commas, or any other punctuation.

Syntax:

Value = Eval#(Number$)

Where:

Number$ contains a string of digits and possibly punctuation such as commas and dollar signs, and Value receives its actual value.

Comments:

Eval simply steps through all of the characters in a string discarding those that are not valid digits. However, it does employ some intelligence in that only one minus sign or decimal point will be recognized.

Example Location:

Project File	Form/BASIC File	Procedure

COMPINT.MAK	CINT.FRM	PV2T_LostFocus


ExpandTab

Visual Basic function contained in FNOTHER.BAS

Purpose:

ExpandTab accepts an incoming text string that contains embedded Chr$(9) tab characters, and replaces them with an appropriate number of Chr$(32) spaces.

Syntax:

Expanded$ = ExpandTab$(Original$, NumSpaces%)

Where:

Original$ is the original string that contains Tabs, NumSpaces% indicates where the tab stops are located, and Expanded$ receives the expanded result.

Comments:

Rather than simply replacing each tab character with a specified number of spaces, ExpandTab takes into account the physical position of the tabs within the string.

The true purpose of a tab is to position the cursor (or a print head) to the next tab position. Therefore, the number of blanks that are being substituted will vary depending on the location of the tab within the string.

ExpandTab takes this into account as it expands the string. Further, you may specify at what intervals the tab stops are to be located. However, tab stops are usually placed at every eighth position.

See Also:

ShrinkTab


F2C

Visual Basic function contained in FNOTHER.BAS

Purpose:

F2C will convert a Fahrenheit temperature to its Centigrade equivalent.

Syntax:

CTemp! = F2C!(FTemp!)

Where:

FTemp! is a valid Fahrenheit temperature, and CTemp! receives an equivalent in degrees Centigrade.

Comments:

Also see the companion function C2F! that converts from Centigrade to Fahrenheit.

Example Location:

Project File	Form/BASIC File	Procedure

DEMONFN.MAK	FNNUM.FRM	F2CPB_Click


LastFirst

Visual Basic function contained in FNOTHER.BAS

Purpose:

LastFirst will reverse the position of a first name and last name in a string such that the last name comes before the first.

Syntax:

NewName$ = LastFirst$(OldName$)

Where:

OldName$ is in the form "John Smith", and NewName$ is converted to "Smith, John".

Comments:

Only one possible problem you may encounter when using LastFirst is when the name contains a suffix such as "Jr.". We could have trapped for that case, but that still wouldn't prevent a "Sr." or "Esq." from slipping through. Further, testing many cases would slow down the function.

Consider this one as a foundation, and simply add any code you feel is necessary to handle the types of names you anticipate. However, LastFirst will correctly handle three word names such as "John A. Smith" or "John Anthony Smith".

See Also:

The companion function LastLast places a last name after the first.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOSFN.MAK	FNSDEMO.FRM	LFirstPB_Click


LastLast

Visual Basic function contained in FNOTHER.BAS

Purpose:

LastLast will reverse the position of a first name and last name in a string such that the last name comes after the first.

Syntax:

NewName$ = LastLast$(OldName$)

Where:

OldName$ is in the form "Smith, John", and NewName$ is then assigned to "John Smith".

Comments:

Also see the companion function LastFirst that places a last name before the first.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOSFN.MAK	FNSDEMO.FRM	LLastPB.Click


Num2Bin and Num2Bin2

Assembler functions contained in QPRO200.DLL

Purpose:

Num2Bin will convert a number into an equivalent binary string with a fixed length of 16 digits. Num2Bin2 performs the same function, but returns only as many digits as required to represent the number.

Declare Syntax:

Declare Function Num2Bin$ Lib "QPRO200.DLL" (ByVal Number%)

Declare Function Num2Bin2$ Lib "QPRO200.DLL" (ByVal Number%)

Call Syntax:

Bin$ = Num2Bin$(Number%)

Bin$ = Num2Bin2$(Number%)

Where:

Number% is an integer value, and Binary$ receives an equivalent string containing only ones and zeros.

Comments:

Num2Bin and Num2Bin2 are designed as integer functions, therefore the values they accept will be considered to be "signed". That is, Visual Basic always considers integer values larger than 32767 as negative numbers.

See Also:

The complementary function Bin2Num.

Example Location:

Project File	Form/BASIC File	Procedure

FILEINFO.MAK	FILEINFO.FRM	Command1_Click


Pad

Visual Basic function contained in FNOTHER.BAS

Purpose:

Pad will add leading zeros to a number, padding it to a specified number of digits.

Syntax:

Padded$ = Pad$(Number!, Digits%)

Where:

Number! is any single precision value, Digits% specifies how many total digits are to be used, and Padded$ receives the result. If the number of digits is too few to accommodate the number, Pad$ will append a leading percent sign.

Comments:

Pad$ is designed to expect a single precision incoming value, but it may be easily changed to either long integer, single, or double precision if you prefer.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOFN.MAK	FNUM.FRM	PadPB_Click


Parse

Visual Basic subprogram contained in FNOTHER.BAS

Purpose:

Parse will extract individual components from a single string, and place each into a separate element of a string array.

Syntax:

Call Parse(Work$, Delim$, Array$())

Where:

Work$ contains the string to be parsed, Delim$ holds a list of the delimiters that separate each component, and Array$() receives each item.

Comments:

Parse is intended primarily to be used for isolating the individual components of Command$. It may also be used to determine the various directories in a PATH, or anywhere that a single string contains multiple items. Even though Parse is actually a called subprogram, it seems most appropriate to place it within the file of related functions.

It is up to you to first dimension the string array that will receive the items. This would be done with the complementary function Delimit.

The example below shows how to combine Parse and Delimit to isolate command line switches such as "/T", "-U", ",3", and so forth.

Work$ = Command$

Delim$ = "-,;_ "	'some common delimiters (last one is a blank)

X = Delimit%(Work$, Delim$) + 1	'see how many matching delimiters there are

ReDim Array$(X)	'+1 is needed to account for the last item

Parse Work$, Delim$, Array$()	'Parse fills the array

For X = 1 To X	'print each item

    Print Array$(X)

Next

Because Delimit returns the number of delimiters rather than the actual number of items, the string array must be dimensioned to one more than the value returned by Delimit.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOSFN.MAK	FNSDEMO.FRM	Command1_Click


ParseStr

Visual Basic function contained in FNOTHER.BAS

Purpose:

ParseStr accepts an incoming string that contains numbers separated by commas, and returns a new string consisting of the equivalent ASCII characters.

Syntax:

Code$ = ParseStr$(List$)

Where:

List$ contains a list of numbers in the form:

"27, 69, 27, 72"

or

"27,69,27,72"

and Code$ receives the equivalent ASCII characters:

Chr$(27) + Chr$(69) + Chr$(27) + Chr$(72)

Comments:

ParseStr$ is primarily intended to allow you to accept a string of printer turn-on or turn-off codes that were entered by a user, and quickly convert them to the appropriate string for sending to a printer.

See Also:

UnParseStr


QPHex

assembler function contained in QPRO200.DLL

Purpose:

QPHex is a replacement for Visual Basic's Hex$ function, that returns a string padded to a specified

number of digits.

Declare Syntax:

Declare Function QPHex$ Lib "QPRO200.DLL" (Value As Any, ByVal Digits%)

Call Syntax:

HexNumber$ = QPHex$(Value, Digits%)

Where:

Value is either an integer or long integer value, and NumDigits% is the number of digits QPHex is to

return.

Comments:

Because QPHex has been designed as a function, it must be declared before it may be used. When declaring QPHex, you should use the As Any clause, which allows you to pass either integer or long integer numbers when you use it.

If the number of digits is between 1 and 4, then QPHex assumes the incoming value is a two-byte integer. Specifying NumDigits% as 5 through 8 instead tells QPHex that the number is a four-byte long integer. The output for two sample uses is shown below:

Print QPHex$(12, 2) 	'this prints "0C"

Print QPHex$(123456&, 8)	'this prints "0001E240"


ShrinkTab

Visual Basic function contained in FNOTHER.BAS

Purpose:

ShrinkTab reduces the length of a string by replacing groups of blank spaces with Chr$(9) tab characters.

Syntax:

Small$ = ShrinkTab$(Original$, NumSpaces%)

Where:

Original$ is the original string that contains groups of blank spaces, NumSpaces% indicates where the

tab characters are to be placed, and Small$ receives the new string that has been reduced in length.

Comments:

Rather than simply replace each group of spaces with a tab character, ShrinkTab takes into account the physical position at which the tabs should be placed within the string.

The true purpose of a tab is to position the cursor (or a print head) at the next tab location. Therefore, the placement of each tab that is being assigned will vary, depending on the location of the original blanks. ShrinkTab takes this into account as it reduces the string. Further, you may specify at what intervals the tab stops are to be located. However, tab stops are usually placed at every eighth position.

See Also:

ExpandTab


Signed

Visual Basic function contained in FNOTHER.BAS

Purpose:

Signed takes an incoming unsigned long integer value, and returns it in a signed form.

Syntax:

S% = Signed%(US&)

Where:

US& is a number within the range 0 to 65535, and S% receives a signed equivalent.

Comments:

The difference between a signed integer value and an unsigned one is really more a matter of semantics than anything else. In either case, there are 65536 values being considered.

When considered on an unsigned basis, integer numbers will range between zero and 65535. But when the same numbers are treated as signed, they instead range from -32768 to 32767. For example, -1 is the same as 65535, -2 equals 65534, and so forth. By definition, a signed number is negative if_when considered in binary_its most significant bit is set.

See Also:

UnSigned

Example Location:

Project File	Form/BASIC File	Procedure

DEOMNFN.MAK	FNUM.FRM	SignedPB_Click


UnParseStr

Visual Basic function contained in FNOTHER.BAS

Purpose:

UnParseStr accepts an incoming string that contains ASCII characters, and returns the equivalent numeric values separated by commas.

Syntax:

List$ = UnParseStr$(Code$)

Where:

Code$ contains a string of ASCII characters in the form:

Chr$(27) + Chr$(69) + Chr$(27) + Chr$(72)

and List$ receives a list of equivalent numbers:

"27,69,27,72"

Comments:

UnParseStr$ allows editing a string of printer turn-on or turn-off codes that had previously been converted using the companion ParseStr function.

Comments in the source code show how to have UnParseStr place an extra space between the characters:

"27, 69, 27, 72"

See Also:

ParseStr


UnSigned

Visual Basic function contained in FNOTHER.BAS

Purpose:

UnSigned takes an incoming signed integer value, and returns it in an unsigned form.

Syntax:

US& = UnSigned&(S%)

Where:

S% is a number within the range -32768 to 32767, and US& receives an unsigned equivalent.

Comments:

The difference between signed and unsigned numbers is discussed in the description of the Signed function.

Example Location:

Project File	Form/BASIC File	Procedure

DEMONFN.MAK	FNNUM.FRM	UnSignedPB_Click


Mathematical Functions

Visual Basic functions contained in FNSPREAD.BAS

QPACOS

Purpose:

Arc cosine of x (rad.) -1 < = x < = +1

Formula:

for Abs(x#) <> 1

QPACOS#(x#) = pi / 2 - Atn(x# / Sqr(1 - x# * x#))

otherwise, if x = 1

 QPACOS#(x#) = 0

else:

QPACOS#(x#) = pi

Equivalent spreadsheet function:

@ACOS(x)

QPASIN

Purpose:

Arc sine of x (rad.) -1 <= x <= +1

Formula:

for Abs(x#) <> 1

PASIN#(x#) = Atn(x# / Sqr(1 - x# * x#))

otherwise

PASIN#(x#) = Sgn(x#) * pi / 2

Equivalent spreadsheet function:

ASIN(x)

QPATAN2

Purpose:

4-quadrant arc tangent of y/x (rad.) 0 < x < 1

Formula:

for x = 0

PTAN2# = Sgn(y#) * pi / 2

for x > 0

PATAN2#(x#, y#) = Atn(y# / x#)

for x < 0 and y => 0

QATAN2#(x#, y#) = pi + Atn(y# / x#)

for x < 0 and y < = 0

PATAN2#(x#, y#) = -pi + Atn(y# / x#)

Equivalent spreadsheet function:

@ATAN2(x, y)

QPLOG10

Purpose:

Log of x base 10

Formula:

PLOG10#(x#) = Log(x#) / Log(10)

Equivalent spreadsheet formula:

LOG(x)

QPROUND

Purpose:

x rounded to n decimal places

Syntax:

Number$ = QPROUND$(Number#, Places%)

Equivalent spreadsheet function:

@ROUND(x, n)


Financial Functions

Visual Basic functions contained in FNSPREAD.BAS

Many financial functions involve annuities. An annuity is simply a series of equal payments made at regular intervals of time. It is a compound interest situation with regular payments. When the payments are made at the beginning of the payment period, the annuity is called an "annuity due".   And when payment is due at the end of the payment period it is an "ordinary annuity".

Where:

   fv# =            future value

       pv# =            present value

       pmt# =          payment per period

       I! =               interest rate per period in percent

       intr# =           I!/100 (I)

       term % =       number of periods

       prin# =          principal (same as pv#)

       bal# =            balloon payment (may be 0)

Several of the functions we have provided are not available even in Lotus 123. These are identified with

an "*".

All of the financial functions are demonstrated in the following example programs:

COMPINT.MAK	DEPR.MAK	IRATE.MAK

CINT.FRM	DEP.MAK	RATE.FRM

QPCINT@ 	QPDDB@	QPRATE#

QPCTERM@	QPSLN@	QPSYD@

OANNUITY.MAK	SFUND.MAK

ANNUITY .FRM	SFUND .FRM

QPPV@	QPFV@

QPPVD@	QPFVD@

QPPVN#	QPFVP@

QPPVND#	QPFVPD@

QPPMT@	QPFVN#

QPPMTD@	QPFVND#

All QPxx routines are in FNSPREAD.BAS.

See Also:

Sinking Fund Annuities, Annuity Due, Ordinary Annuity

Annuity Due Relationships, Other Compound Interest Relationships


Sinking Fund Annuities

Sinking Fund Annuities:

A sinking fund annuity is most easily described as a savings fund designed to accumulate a predetermined amount of money by a specified date.

QPFV

Future value of ordinary annuity (sinking fund)

Formula:

QPFV#(pmt#, intr#, term%) = pmt# * ((1 + intr#)^ term% - 1) /intr#

Equivalent spreadsheet function:

@FV(pmt#, intr#, term%)

QPFVN

Term (number of payments) of a sinking fund

Formula:

QPFVN#(fv#, pmt#, intr#) = Log(fv# * intr# / pmt# + 1) / Log(1 + intr#)

QPFVP

Payment amount of a sinking fund

Formula:

QPFVP#(fv#, intr#, term%) = fv# * intr# / ((1 + intr#)^ term% - 1)


Annuity Due

Annuity due:

An example annuity due (future value) is the future value of a savings account with equal deposits made at the beginning of each period.

QPFVD

Future value of annuity due

Formula:

QPFVD#(pmt#, intr#, term%) = pmt# * (1 + intr#) * ((1 + intr#)^ term% - 1) / intr#

QPFVND

Term (number of payments) of an annuity due/FV

Formula:

QPFVND#(fv#, pmt#, intr#) = LoG(fv# * intr# / pmt# + 1 + intr#) / Log(1 + intr#) - 1

QPFVPD

Payment amount of an annuity due/FV

Formula:

QPFVPD#(fv#, intr#, term#) = fv# / ((1 + intr#) / intr# * (1 + intr#)^ term% -1)


Ordinary Annuity

Ordinary Annuity:

When a sum of money is to be repaid with interest in fixed payments for a specified number of periods (such as with a home mortgage), it is called an ordinary annuity. A balloon payment may be associated with this type of annuity.

QPPMT

Loan payment (ordinary annuity)

Formula:

QPPMT#(pv#, intr#, term%, bal#) = (pv# - bal# * (1+intr#)^ -term%) / _

     ((1 -(1+intr#)^ -term%) / intr#)

Equivalent spreadsheet function:

@PMT(pv#, intr#, term%)

QPPV

Present value of an ordinary annuity

Formula:

QPPV#(pmt#, intr#, term%, bal#) = pmt# * _

     (1 - (1 + intr#)^ -term%) / intr# + bal# * (1 + intr#)^ -term%

Equivalent spreadsheet function:

@PV(pmt#, intr#, term%)

QPPVN

Term (number of payments) of an ordinary annuity

Formula:

QPPVN#(pmt#, intr#, pv#, bal#) Log((pmt# - intr# * bal#) / _

      (pmt# - intr# * pv#)) / Log(1 + intr#)


Annuity Due Relationships

Annuity due relationships:

In order to find the present value of a lease which will involve fixed payments at the beginning of each payment period, use these annuity due relationships.

QPPMTD

Lease payment (annuity due)

Formula:

QPPMTD#(pv#, intr#, term%, bal#) = (pv# - bal# * (1 + intr#)^ -term%) / (1 + intr#) / _

     ((1 - (1 + intr#)^ -term%) / intr#)

QPPVD

Present value of annuity due

Formula:

QPPVD#(pmt#, intr#, term%, bal#) = pmt# * (1+intr#) * (1-(1+intr#)^ -term%) / intr# + bal# * _

    (1+intr#)^ -term%

QPPVND

Term (number of payments) of an annuity due

Formula:

QPPVND#(pmt#, intr#, pv#, bal#) = Log((pmt# * (1 + intr#) / intr# - bal#) / (pmt# * _

     (1 + intr#) / intr# - pv#))/ Log(1 + intr#)


Other Compound Interest Relationships

QPCINT

Compounded interest

Purpose:

To find the future value of a savings account drawing compound interest.

Formula:

QPCINT#(pv#, intr#, term%) = pv# * (1 + intr#)^ term%

QPCTERM

Compounded term of investment

Purpose:

To determine the number of compounding periods it will take an investment to grow to a pre-determined value.

Formula:

QPCTERM#(pv#, fv#, intr#) = Log(fv# / pv#) / Log(1 + intr#)

Equivalent spreadsheet function:

@CTERM(intr#, fv#, pv#)

NOTE:

Interest Rate (intr#) must be in decimal form. For example, the monthly rate of 10% per year is (10 / 100) / 12 or .008333.

QPIRR

Internal rate of return

Usage:

QPIRR#(intr#, Array#())

This function is seeded by giving it an initial IRR rate (guess). The algorithm first brackets the correct IRR and then converges on the final IRR by a halving method. Convergence ends when a given degree of accuracy is reached.

Equivalent spreadsheet function:

@IRR(guess#, list)

QPNPV

Net present value of future cash flows

Usage:

QPNPV#(intr#, Array#())

NOTE:

If the initial flow is not an outflow, enter zero as the first value in Array#(). This will give the PV of the  dollar flow.

Equivalent spreadsheet function:

@NPV(intr#, list)

QPRATE

Rate of investment

Purpose:

To obtain the periodic interest rate required for an investment to grow to a pre-determined value in a specified time.

Formula:

QPRATE#(pv#, fv#, term%) = (fv# / pv#)^ (1 / term%) - 1

Equivalent spreadsheet function:

@RATE(fv#, pv#, term%)


Depreciation
Depreciation:

Visual Basic functions for depreciation calculation

The IRS allows depreciation of various assets using some of the methods listed below (depending on the type and life of the asset).

NOTE:

In all cases, year must be greater than or equal to 1 and less than or equal to the depreciable life of the

asset.

QPDDB

Double declining balance depreciation

Formula:

QPDDB#(cost#, sal#, life%, per%, m!) = m! * cost# / life%^ per% * (life% - 2)^ (per% - 1)

Where:

"m" is the depreciation multiplier, ie.

     m = 2_Double Declining Balance

     m = 1.5_150% Declining Balance

     m = 1_Simple Declining Balance

Note:

Adjustments are made to the formula within the function to insure that the total depreciation does not exceed total cost less salvage value.

Equivalent spreadsheet function:

@DDB(cost#, salvage#, life%, period%)

All depreciation functions are in:

Project File	Form/BASIC File

COMPINT.MAK	FNSPREAD.BAS

QPSLN

Straight-line depreciation

Formula:

QPSLN#(cost#, sal#, life%) = (cost# - sal#) / life%

Equivalent spreadsheet function:

@SLN(cost#, salvage#, life%)

QPSYD

Sum-of-years'-digits depreciation

Formula:

QPSYD#(cost#, sal#, life%, per%) = (cost# - sal#) * _

     (life% - per% + 1) / (life% * (life% + 1) / 2)

Equivalent spreadsheet function:

@SYD(cost#, salvage#, life%, period%)


Statistical Functions

Visual Basic functions contained in FNSPREAD.BAS

DEMOSTAT.MAK demonstrates the following functions:

   STAT.FRM

   QPAVG#

   QPCOUNT#

   QPMAX#

   QPMIN#

   QPSTD#

   QPSUM#

   QPVAR#

QPAVG

Returns the average of the values in an array

Usage:

Average = QPAVG#(Array#())

Formula:

QPAVG#(Array#()) = QPSUM#(Array#()) / QPCOUNT%(Array#())

Equivalent spreadsheet function:

@AVG(list)

QPCOUNT

Returns the number of entries in an array

Usage:

Count = QPCOUNT%(Array#())

Formula:

QPCOUNT%(Array#()) = UBound(Array#, 1) - LBound(Array#, 1) + 1

Equivalent spreadsheet function:

@COUNT(list)

QPMAX

Returns the highest value in a list

Usage:

MaxValue# = QPMAX#(Array#())

Note: This Double Precision function calls an assembly language subroutine that quickly scans the array for the highest value. Assembler subroutines are also provided in QuickPak Professional for Windows for currency, integer, long integer, and single precision arrays.

Equivalent spreadsheet function:

@MAX(list)

QPMIN

Returns the lowest value in a list

Usage:

MinValue# = QPMIN#(Array#())

Note: This Double Precision function calls an assembly language subroutine that quickly scans the array for the lowest value. Assembler subroutines are also provided in QuickPak Professional for Windows for currency, integer, long integer, and single precision arrays.

Equivalent spreadsheet function:

@MIN(list)

QPSTD

Population standard deviation of items in list

Usage:

STD# = QPSTD#(Array#())

Equivalent spreadsheet function:

@STD(list)

QPSUM

Returns the sum of all values in an array

Usage:

SUM# = QPSUM#(Array#())

Equivalent spreadsheet function:

@SUM(list)

QPVAR

Population variance of values in list

Usage:

VAR# = QPVAR#(Array#())

Equivalent spreadsheet function:

@@VAR(list)


Menu/Input Routines

AsciiPick and AsciiChart

Calendar

ClrEdit

GetFileName

GetSaveAsName


 AsciiPick and AsciiChart

BASIC Subprograms contained in ASCPICK.BAS and ASCCHART.FRM

Purpose:

AsciiPick and AsciiChart display a dialog box containing all the characters in the ASCII character set.

Syntax:

AsciiPick Char%

Where:

Char% tells AsciiPick which character to highlight when the chart is first displayed, and is returned

holding the ASCII value of the character chosen.  Char% can be preset to -1 telling AsciiPick not to

highlight any character when the chart is first displayed.

Comments:

AsciiPick and AsciiChart are meant to be used as a pop-up utility in your programs.  When called,

AsciiPick displays an ASCII chart containing a list box of all the ASCII characters.  The decimal and hex

ASCII value is shown for each ASCII character from 0 to 255, as well as the printable characters included

in the ANSI character set employed by Windows.  A character can be chosen by highlighting the character

in the list box and clicking OK, or by double clicking in the list box.

Example Location:

Project File	Form/BASIC File

ASCCHART.MAK	ASCCHART.FRM


Calendar

Visual Basic form contained in CSCLNDR.FRM

Purpose:

This form displays a calendar letting the user choose a date.

Syntax:

Calendar.Show

Where:

The form's Tag property sets or reflects the selected date.

Comments:

The Calendar form can be used either as a Modal or Modeless form. That is, if its command button is

visible and you use the Modal argument (1) to display the form, the user will not be able to switch focus to

any other form until a date is selected.  The following code displays the Calendar form in a Modal fashion

and waits for the user to select a date:

Calendar.Command1.Visible = -1

Calendar.Show 1

MsgBox "You Picked " + Calendar.Tag

Calendar can also be displayed as a modeless form where the user may browse or view dates without

having to actually make a selection. This can be done without any additional setup as follows:

Calendar.Show

The Calendar form's Tag property can be used to force the display of a particular date by simply setting it

prior to displaying the form:

Calendar.Tag = "01-01-1992"

Here, the form would display the calendar for the month of January in the year 1992.

When the form is displayed the user can select a day by simply clicking on the appropriate day box.  The

month and year can be selected by moving either the bottom or right scroll bars.  If the form is resized by

the user, the display of its contents will be appropriately adjusted.

When the form is loaded, it checks the user's WIN.INI file to determine how the calendar should be

formatted.  That is, if the sShortDate parameter is set to "M/d/yy" (the U.S. format) then the first day of

each week will start on Sunday.  If any other format is used, weeks will start on Monday.

Note that when a non-U.S. format is used, the day and month labels are displayed in German.  You can

change these by simply searching the code contained in CSCLNDR.FRM for the EuroFlag% variable, and

changing the associated text.

Example Location:

Project File	Form/BASIC File	Location

DEMOCAL.MAK	DEMOCAL.BAS	Main

CSCLNDR.MAK	CSCLNDR.FRM


ClrEdit

Visual Basic form contained in CLREDIT.FRM

Purpose:

Allows you to edit the Visual Basic color palette. A color can be passed for editing, and the editied color

may be returned to the caller.

Syntax:

ClrEdit.Show

Comments:

Once ClrEdit has been displayed, you may edit the color shown in the Sample Color window by moving

the appropriate color slider to reach the desired value. If you wish to select a different color from the

existing palette to edit, simply click on that color and it will replace the current Sample Color. Clicking

Undo will undo the most recent edit.  Clicking Restore will reset all colors to their original values,

including the Color Sample. Once editing is complete, clicking OK will Hide the form and the selected

color can be obtained by reading the value of ClrEdit.ClrSampleL.BackColor.

To pass a color to be edited, simply assign the color to ClrEdit's Sample Color BackColor property before

showing the form. The Sample Color is contained in a label named ClrSampleL.

ClrEdit.ClrSampleL.BackColor = PassedColor&

ClrEdit.Show 1

or

ClrEdit.ClrSampleL.BackColor = Control.BackColor

ClrEdit.Show 1

Note that the argument 1 after Show means the form is modal.

Returning a color to the caller is just as simple. Assign your color to ClrEdit's Sample Color background

property after returning from the editor:

ReturnedColor& = ClrEdit.ClrSampleL.BackColor

or

Control.BackColor = ClrEdit.ClrSampleL.BackColor

Example Location:

Project File	Form/BASIC File

CLRCHART	CLREDIT.FRM


GetFileName

Visual Basic function contained in FILEOPN.BAS

Purpose:

GetFileName displays a dialog box which allows the user to select a file from any directory, on any drive

available to DOS.  It returns the full path and file name selected, and also which button was pressed.

Call Syntax:

Button% = GetFileName%(FileSpec$, FileName$, ButtonName$())

Where:

FileSpec$ is the default file specification which GetFileName uses to find file names for display,

FileName$ is the fully qualified file name returned by GetFileName, and ButtonName$() is an array of

captions to use for optional buttons.  Button% is returned holding the number of the button that was

pressed by the user.

Comments:

The ButtonName$() array can be used to specify extra buttons other than the standard Ok and Cancel

buttons.  If it is simply dimensioned to 0, then the routine will use the default Ok and Cancel buttons.  If

dimensioned to a value greater than 0, the text of each element will be used for its corresponding button

up to a maximum of six.  To use four buttons instead of the two default ones you could use code such as

the following:

DIM ButtonName$(3)

ButtonName$(0) = "Cancel"

ButtonName$(1) = "Replace"

ButtonName$(2) = "Merge"

ButtonName$(3) = "New"

Then to test the user's response, you would do the following:

FileSpec$ = "*.TXT"

Select Case GetFileName%(FileSpec$, FileName$, ButtonName$())

  Case 0

    'User pressed Cancel.

  Case 1

    'Replace current document with contents of file.

  Case 2

    'Merge file with current document.

  Case 3

    'Create new document with selected file.

End Select

If ButtonName$() is dimensioned to 0, the function returns 0 if the user presses Cancel, or 1 if Ok was

pressed. Since GetFileName displays a dialog box, you must be sure to add the FILEOPN.FRM form file

to your project as well as the FILEOPN.BAS code module.

See Also:

GetSaveAsName

Example Location:

Project File	Form/BASIC File	Location

FILEOPN.MAK	DFILEOPN.BAS	Main


 GetSaveAsName

Visual Basic function contained in FILESAV.BAS

Purpose:

GetSaveAsName displays a dialog box which allows the user to specify a file name for saving.

Call Syntax:

Button% = GetSaveAsName%(FileName$)

Where:

FileName$ is the default file name used to save the file, and it also returns the name specified by the user.

Button% receives the number of the button pressed by the user:  0 = Cancel, and 1 = Ok.

Comments:

Since GetSaveAsName displays a dialog box, you must be sure to add the FILESAV.FRM form file to

your project as well as the FILESAV.BAS code module.

See Also:

GetFileName

Example Location:

Project File	Form/BASIC File	Procedure

FILESAV.MAK	DFILESAV.BAS	Main


Keyboard Routines

AltKey

CapsLock

CapsOff and CapsOn

CreateAK

CreateHK

CtrlKey

KillAK

KillHK

NumLock

NumOff and NumOn

ScrolLock

ScrollOn and ScrollOff

ShiftKey


AltKey

assembler function contained in QPRO200.DLL

Purpose:

AltKey reports if the Alt key is currently depressed.

Declare Syntax:

Declare Function AltKey% Lib "QPRO200.DLL" ()

Call Syntax:

Active% = AltKey%()

Where:

Active receives -1 if the Alt key is currently down, or 0 if it is not.

Comments:

AltKey is designed to return -1 for a true value to also allow the use of the Visual Basic Not operator:

If AltKey%() Then . . .

or

If Not AltKey%() Then . . .

Example Location:

Project File	Form/BASIC File	Procedure

KEYBOARD.MAK	KEYBOARD.FRM	TIMER1_TIMER


CapsLock

assembler function contained in QPRO200.DLL

Purpose:

CapsLock reports if the Caps lock key is currently on or off.

Declare Syntax:

Declare Function CapsLock% Lib "QPRO200.DLL" ()

Call Syntax:

Active% = CapsLock%()

Where:

Active receives -1 if the CapsLock key is currently down, or 0 if it is not.

Comments:

CapsLock is designed to return -1 for a true value to also allow the use of the Visual Basic Not operator:

If CapsLock%() Then . . .

or

If Not CapsLock%() Then . . .

Example Location:

Project File	Form/BASIC File	Procedure

KEYBOARD.MAK	KEYBOARD.FRM	TIMER1_TIMER


CapsOff and CapsOn

assembler subroutines contained in QPRO200.DLL

Purpose:

CapsOff turns off the CapsLock key status, and CapsOn turns it on.

Declare Syntax:

Declare Sub CapsOff Lib "QPRO200.DLL" ()

Declare Sub CapsOn Lib "QPRO200.DLL" ()

Call Syntax:

CapsOff

CapsOn

Example Location:

Project File	Form/BASIC File	Procedure

KEYBOARD.MAK	KEYBOARD.FRM	TIMER1_TIMER


CreateAK

assembler function contained in QPRO200.DLL

Purpose:

Creates a system-wide keyboard handler that can be used to monitor keystrokes typed into any running Windows application.

Declare Syntax:

Declare Function CreateAK% Lib "QPRO200.DLL" (ByVal hWnd%)

Call Syntax:

Flag% = CreateAK%(hWnd)

Where:

hWnd is the window handle property of your form, and it is used by  CreateAK to identify it.  The function returns this same handle  in Flag% if it was successful.  If CreateAK has been used before  and KillAK was not used to remove the key handler, CreateAK instead returns -1 to indicate that there is already an active key handler for this form.

Comments:

Once CreateAK has been called, your form's KeyDown event procedure will be activated whenever any key has been typed into any application.  At this point you can interpret the KeyDown  subprogram's two parameters_KeyCode% and Shift%_in the same manner you normally would for this type of event.

See Also:

Please see the KillAK routine which must be called when your application ends.  Also, if you want to trap a limited number of keystrokes, see the CreateHK routine listed elsewhere in this section.  Using CreateAK and KillAK is explained in depth in the "Global Key Processing In Windows" section of Chapter 1 of this manual.


CreateHK

assembler function contained in QPRO200.DLL

Purpose:

Creates a system-wide hot key handler for a specific keystroke combination.

Declare Syntax:

Declare Function CreateHK& Lib "QPRO200.DLL" (ByVal VKCode%, _

  ByVal Mask%, ByVal Wnd%, ByVal UserVK%)

Call Syntax:

hHotKey& = CreateHK&(VKCode%, Shift%, hWnd, UserVK%)

Where:

VKCode% is any valid virtual key code.  These are the same codes  that are normally passed to a KeyDown event procedure.  The Shift parameter is the mask for the shift keys you want included in your  hot key.  Again, this is the same as the Shift parameter passed to  a KeyDown event.

The third parameter is the form's hWnd property value.  This  registers your form with the hot key handler.  Finally, UserVK% is  the value you want sent to your window when your hot key comes up.

hHotKey& is assigned from the function as the handle to the hot key.  This value is used only to disable the hot key later on.

Comments:

The values used for the VKCode% parameter can be found in the  CONSTANT.TXT file that comes with Visual Basic.  All of these  constants have a "KEY_" prefix.  The value used for the Shift%  parameter can represent any combination of Alt, Ctrl, and Shift  keys.  For example, you could define a hot key as Shift-Ctrl-P.

Defining Shift mask values is explained in the Visual Basic Programmer's Guide in the section entitled "Writing Low Level Keyboard Handlers".

The UserVK% parameter can be any value you choose; however, it is best to use values greater than 144 because that is where the normal key codes end.

Note that hHotKey& is returned with a value of -1 if the hot key  you selected is already in use, or -2 if no more hot keys are available for use.  The hot key handler is limited to 1024 simultaneous hot keys.  Also note that hot keys defined in the Windows Macro Recorder are independent of CreateHK.  Please understand that CreateHK cannot detect if a Macro Recorder hot key is already taken by any other Windows application, so be careful of overlap!

Also See:

The KillHK routine must be called when your application ends.  Also, if you want to monitor all keyboard activity, see the CreateAK routine listed elsewhere in this section.  Using CreateHK and KillHK is explained in depth in the "Global Key Processing In Windows" section of Chapter 1 of this manual.

Example Location:

Project File	Form/BASIC File	Procedure

HOTKEY.MAK	HOTKEY.FRM	Form_Load


CtrlKey

assembler function contained in QPRO200.DLL

Purpose:

CtrlKey reports if the Ctrl key is currently depressed.

Declare Syntax:

Declare Function CtrlKey% Lib "QPRO200.DLL" ()

Call Syntax:

Active% = CtrlKey%()

Where:

Active receives -1 if the Ctrl key is currently down, or 0 if it is not.

Comments:

CtrlKey is designed to return -1 for a true value to also allow the use of the Visual Basic Not operator:

If CtrlKey%() Then . . .

or

If Not CtrlKey%() Then . . .

Example Location:

Project File	Form/BASIC File	Procedure

KEYBOARD.MAK	KEYBOARD.FRM	TIMER1_TIMER


KillAK

assembler subroutine contained in QPRO200.DLL

Purpose:

Removes from memory a system-wide keystroke handler that was created with the CreateAK routine.

Declare Syntax:

Declare Sub KillAK Lib "QPRO200.DLL" (ByVal hWnd%)

Call Syntax:

KillAK hWnd

Where:

hWnd is the window handle property of your form, and it is used by KillAK to identify the handler created previously by CreateAK.

Comments:

If your program uses CreateAK to create a keystroke handler, you must call KillAK before your program ends.  This ensures that the special keyboard hook function installed by CreateAK is removed from memory.  This also allows your program to stop trapping keys when for instance you are done recording a keystroke macro. The use of CreateAK and KillAK is explained in depth in the "Global Key Processing In Windows" section of Chapter 1 of this manual.


KillHK

assembler subroutine contained in QPRO200.DLL

Purpose:

Removes from memory a system-wide hot key handler that was created by CreateHK.

Declare Syntax:

Declare Sub KillHK Lib "QPRO200.DLL" (ByVal hHotKey&)

Call Syntax:

KillHK hHotKey&

Where:

hHotKey& is the handle of hot key defined using the CreateHK routine.

Comments:

If your program uses CreateHK to create a hot key handler, you  must call KillHK before your program ends.  This ensures that the special keyboard hook function installed by CreateHK is removed from memory.

The use of CreateHK and KillHK is explained in depth in the "Global Key Processing In Windows" section of Chapter 1 of this manual.

Example Location:

Project File	Form/BASIC File	Procedure

HOTKEY.MAK	HOTKEY.FRM	Form_Unload


NumLock

assembler function contained in QPRO200.DLL

Purpose:

NumLock will quickly tell if the NumLock key is currently on or off.

Declare Syntax:

Declare Function NumLock% Lib "QPRO200.DLL" ()

Call Syntax:

Active% = NumLock%()

Where:

Active% receives -1 if the NumLock key is currently down, or 0 if it is not.

Comments:

NumLock is designed to return -1 for a true value to allow the use of the Visual Basic Not operator:

If NumLock%() Then . . .

or

If Not NumLock%() Then . . .

Example Location:

Project File	Form/BASIC File	Procedure

KEYBOARD.MAK	KEYBOARD.FRM	TIMER1_TIMER


NumOff and NumOn

assembler subroutines contained in QPRO200.DLL

Purpose:

NumOff turns off the NumLock key status, and NumOn turns it on.

Declare Syntax:

Declare Sub NumOff Lib "QPRO200.DLL" ()

Declare Sub NumOn Lib "QPRO200.DLL" ()

Call Syntax:

NumOff

NumOn

Example Location:

Project File	Form/BASIC File	Procedure

KEYBOARD.MAK	KEYBOARD.FRM	TIMER1_TIMER


ScrolLock

assembler function contained in QPRO200.DLL

Purpose:

ScrolLock reports if the Scroll lock key is currently on or off.

Declare Syntax:

Declare Function ScrolLock% Lib "QPRO200.DLL" ()

Call Syntax:

Active% = ScrolLock%()

Where:

Active% receives -1 if the Scroll lock key is currently active, or 0 if it is not.

Comments:

ScrolLock is designed to return -1 for a true value to also allow the use of the Visual Basic Not operator:

If ScrolLock%() Then . . .

or

If Not ScrolLock%() Then . . .

Example Location:

Project File	Form/BASIC File	Procedure

KEYBOARD.MAK	KEYBOARD.FRM	TIMER1_TIMER


ScrollOn and ScrollOff

Assembler subroutines contained in QPRO200.DLL

Purpose:

ScrollOn turns on the Scroll lock key, and ScrollOff turns it off.

Declare Syntax:

Declare Sub ScrollOn Lib "QPRO200.DLL" ()

Declare Sub ScrollOff Lib "QPRO200.DLL" ()

Call Syntax:

ScrollOn

ScrollOff

Example Location:

Project File	Form/BASIC File	Procedure

KEYBOARD.MAK	KEYBOARD.FRM	ScrollLockL_Click


ShiftKey

assembler function contained in QPRO200.DLL

Purpose:

ShiftKey reports if either Shift key is currently depressed.

Declare Syntax:

Declare Function ShiftKey% Lib "QPRO200.DLL" ()

Call Syntax:

Active% = ShiftKey%()

Where:

Active% receives -1 if either Shift key is currently down, or 0 if neither is.

Comments:

ShiftKey is designed to return -1 for a true value to also allow the use of the Visual Basic Not operator:

If ShiftKey%() Then . . .

or

If Not ShiftKey%() Then . . .

Example Location:

Project File	Form/BASIC File	Procedure

KEYBOARD.MAK	KEYBOARD.FRM	TIMER1_TIMER


Miscellaneous Routines

AddUSI

BCopy

BCopyT

BitGet0

BitPack0

BitSet0

BitUnPack0

Compare

CompareT

CSMaximum

CSMinimum

CvC,D,I,L,S

CvDMbf and CvSMbf

Date2Day

Date2Num

DayName

EDate2Num

ENum2Date

Evaluate

Factorial

Fre

GetCPU

GetEquip

HiWord

Inp

Inp2

LoWord

LPStr

MakeLong

MathChip

MaxInt and MaxLong

MinInt and MinLong

MkC,D,I,L,S

MkSMbf and MkDMbf

MonthName

NetName

NetType

NetTypeName

Num2Date

Num2Day

Num2Time

Out

Out2

Peek1

Peek2

Play

Poke1

Poke2

Power and Power2

QPSegAdr

QPSolver

QPUSI

QPWeekDay

ReadZipX

SAdd

ShiftIL and ShiftIR

ShiftLL and ShiftLR

Sound

Soundex

SSeg

Swap

SysTime

Time2Num

Times2

TrapInt

VarPtr

VarSeg


AddUSI

Assembler function contained in QPRO200.DLL

Purpose:

AddUSI adds two integers on an unsigned basis, without creating an overflow error if the total exceeds 32767.

Declare Syntax:

Declare Function AddUsi% Lib "QPRO200.DLL" (ByVal Int1%, ByVal Int2%)

Call Syntax:

Sum% = AddUsi%(X%, Y%)

Where:

X% and Y% are the integers to be added, and Sum% receives the result.

Comments:

In Microsoft Visual Basic, integer numbers and variables may range from -32768 to 32767, which is a total of 5536 possible values. However, assembler routines can optionally consider this same range of values as spanning from  0 to 65535. Therefore, a variable's address that is greater than 32767 will be reported by VarPtr as a negative value. This behavior can cause problems when addresses that may exceed 32767 are being manipulated. For example, if a Type variable begins at address 32765 and 10 is added to access a portion of the Type, Visual Basic will create an Overflow error.

AddUSI simply adds the two values in assembly language, which of course does not generate errors. Even if the result exceed 65535, the adding will merely "wrap" around and pass zero. AddUSI is invaluable in those situations where Visual Basic might create an error even when the values are legitimate.


BCopy

assembler subroutine contained in QPRO200.DLL

Purpose:

BCopy will copy a block of memory (up to 64K in size) to a new location.

Declare Syntax:

Declare Sub BCopy Lib "QPRO200.DLL" (ByVal FromSeg%, ByVal FromAddr%, ByVal _

ToSeg%, ByVal ToAddr%, ByVal NumBytes%, ByVal Direction%)

Call Syntax:

BCopy FromSeg%, FromAddr%, ToSeg%, ToAddr%, NumBytes%, Direction%

Where:

FromSeg% and FromAddr% indicate the source location of the block, and ToSeg% and ToAddr% tell where to copy it to. NumBytes% is, well, you can figure that one out. Direction% is either 0 to copy in the forward direction, or -1 to copy backwards.

Comments:

BCopy is useful in a variety of situations, for example if you need to make a copy of an array or duplicate a range of elements.

The number of bytes may be up to 65535, though you will have to use a long integer (or a negative number) to specify a value greater than 32767.

BCopy could also be used to swap strings in and out of a dynamic array in far memory to conserve string space, as shown below.

   X$ = "This is a test"               'Make a test string

   L% = Len(X$)                        'Remember its length

   Dim A%(L% \ 2)                      'Make an array to hold it

   BCopy SSeg%(X$), SAdd%(X$), VarSeg%(A%(1)), VarPtr%(A%(1)), L%, 0

   N$ = Space$(L%)                      'Make a new string

   BCopy VarSeg%(A%(1)), VarPtr%(A%(1)), SSeg%(N$), SAdd%(N$), L%, 0

   Print N$                             'Show that it worked

Here we're using the QuickPak Professional SSeg and SAdd functions to locate the string in memory, and VarSeg and VarPtr to locate the array.  BCopy may then be used both to copy the string to the array, as well as back again when the string is needed.

Example Location:

Project File	Form/BASIC File	Procedure

CLRCHART.MAK	CLREDIT.FRM	DefaultPB_Click


BCopyT

assembler subroutine contained in QPRO200.DLL

Purpose:

BCopyT will copy one or more elements in a Type array to another array, or to any location in memory. Alternately, BCopyT can be used to move any contiguous block of memory.

Declare Syntax:

Declare Sub BCopyT Lib "QPRO200.DLL" (FromEl As Any, ToEl As Any, _

  ByVal ElSize%, ByVal NumEls%)

Call Syntax:

BCopyT FromEl, ToEl, ElSize%, NumEls%

Where:

FromEl is the starting element of the source array and ToEl is the starting element in the destination array. ElSize% is the size of each element in bytes, or a special code (see below). NumEls% is the number of elements to be copied.

Comments:

Where the original BCopy routine is intended for copying to and from any location in memory, BCopyT is designed to simplify the calling syntax when Type variables and array elements are being moved.

The syntax shown above is for copying one or more elements in a numeric or Type array.  The actual number of bytes to copy is calculated within BCopyT by multiplying ElSize% times NumEls%. If you intend to copy 32K bytes or less (32768), simply set ElSize% to the number of bytes, and use 1 for NumEls%. To copy, say, 64K, you could set ElSize% to 32768 and use 2 for NumEls%. Any similar combination will also work.

BCopyT also recognizes the special codes used by the various Type sort routines for ElSize%, as shown in the table below.

    -1         2-byte integer

    -2         4-byte long integer	}these two are the same,

    -3         4-byte single precision	}these are interchangeable

    -4         8-byte double precision	}these two are the same,

    -5         8-byte currency	}and are interchangeable.

    +n        type variable of length n


BitGetO
BitGet0

assembler function contained in QPRO200.DLL

Purpose:

Tests a bit within a block of data.

Declare Syntax:

Declare Function BitGet0% Lib "QPRO200.DLL" (Flags As Any, _

ByVal Bit%)

Call Syntax:

Truth% = BitGet0%(Flags, Bit%)

or

Truth% = BitGet0%(ByVal Flags$, Bit%)

Where:

Flags is a block of data such as a numeric variable, or fixed length string of up to 8192 bytes (65536 bits).  Note that Variants cannot be used.  Bit% is the bit to test.  Note that the first bit position is specified as 0.  Truth% receives either True (-1), or False (0) depending on whether or not the bit is currently set.

Comments:

While you can use Basic code to test a bit within a variable, BitGet0 is more convenient, more readable. In Visual Basic:

If Flags% And 2 ^ Bit% Then ...

vs.

If BitGet0%(Flags%, Bit%) Then ...

Another advantage to this routine is that non-integer variable types can be used to hold the flag values such as floating point numbers, and strings.  Note that if you use a string, you must pass it by value as follows:

If BitGet0%(ByVal Flags$, Bit%) Then ...

See the comments under BitSet0 for more information on storing multiple Boolean values in a single variable.

See Also:

BitPack0, BitSet0, BitUnPack0


BitPackO
BitPack0

assembler function contained in QPRO200.DLL

Purpose:

Sets a range of bits within a block of data to a specified value.

Declare Syntax:

Declare Function BitPack0% Lib "QPRO200.DLL" (Flags As Any, ByVal StartBit%,_

  ByVal NumBits%, ByVal NewBits%)

or

Declare Sub BitPack0% Lib "QPRO200.DLL" (Flags As Any, ByVal StartBit%, _

  ByVal NumBits%, ByVal NewBits%)

Call Syntax:

PrevVal% = BitPack0%(Flags, StartBit%, NumBits%, NewBits%)

or:

BitPack0 Flags, StartBit%, NumBits%, NewBits%

or for strings:

PrevVal% = BitPack0%(ByVal Flags$, StartBit%, NumBits%, NewBits%)

Where:

Flags is a block of data such as a numeric variable, or fixed length string of up to 8192 bytes (65536

bits).  Note that Variants cannot be used.  StartBit% is the first bit to set, NumBits% is the number of bits

to set, and NewBits% contains the bits to be set into the specified position.  Note that the first bit position

is specified as 0.

PrevVal% receives the previous value of the specified bits.  If you are not concerned with the previous

value, you can declare the routine as a Sub, and ignore the return value.

Comments:

As discussed under the comments section of the BitSet0 routine, it is often convenient to store several

Boolean values in a single variable such as an integer.  While BitSet0 allows you to set a single bit within

a block of data, BitPack0 lets you set several bits at one time thus allowing you to create and manage

groups of bits within a variable.  This technique is commonly referred to as "bit packing".  The BitPack

routine can be thought of as a Mid$ routine for bits instead of characters.  With this technique you might

decide that the first three bits within an integer will be used to store a group of related flags, and that the

next 8 represent a numeric value.  The following example illustrates this:

     '--- Set bits within the first group of three bits

     SetBit0 NewBits%, 0, True        'Set the first and third bits to true

     SetBit0 NewBits%, 2, True

     StartBit% = 0                    	'Start at the first bit

     NumBits% = 3                    	 'Set 3 bits at once

     '--- Set the first 3 bits of the integer

     BitPack0 Flags%, StartBit%, NumBits%, NewBits%

     '--- Now save a number into the remaining bits within the integer

     NewBits% = 128                   	'Store the number 128

     StartBit% = 3                    	'  starting at bit 3 (4th position)

     NumBits% = 8                     	'  using 8 bits from NewBits%

     '--- Store it in Flags%

     BitPack0 Flags%, StartBit%, NumBits%, NewBits%

To later unpack the variable, use the companion BitUnPack0 function.

Note that you can also use non integer variable types to store greater quantities of information than can

be packed into either an integer, or long integer such as floating point numbers, and strings.  Note that if

you use a string, you must pass it by value as follows:

    BitSet0%(ByVal Flags$, Bit%, NewVal%)

See Also:

BitGet0, BitSet0, BitUnPack0


BitSetO
BitSet0

assembler function contained in QPRO200.DLL

Purpose:

Sets or clears a bit within a block of data.

Declare Syntax:

Declare Function BitSet0% Lib "QPRO200.DLL" (Flags As Any, ByVal Bit%, _

  ByVal NewVal%)

or

Declare Sub BitSet0 Lib "QPRO200.DLL" (Flags As Any, ByVal Bit%, ByVal NewVal%)

Call Syntax:

PrevVal% = BitSet0%(Flags, Bit%, NewVal%)

or:

BitSet0 Flags, Bit%, NewVal%

or for strings:

PrevVal% = BitSet0%(ByVal Flags$, Bit%, NewVal%)

Where:

Flags is a block of data such as a numeric variable, or fixed length string of up to 8192 bytes (65536

bits).  Note that Variants cannot be used.  Bit% is the bit to set (the first bit is specified as 0), and

NewVal% is the value (0, or -1) to set it to.  PrevVal% receives the previous value of the bit.  If you are

not concerned with the previous value, you can declare the routine as a Sub, and ignore the return value.

Comments:

It is often convenient to store several Boolean (True/False) values within a single variable rather than

create separate variables for each one.  For instance, an integer can be thought of as an array of 16 bits

where each bit represents a flag value.  In this case, the savings would be 30 bytes if all 16 bits were

used.  If you are storing Boolean values in a database, the savings in disk space can be very substantial.

In addition, it is more convenient to pass a single variable to a procedure than many separate flag

variables.

While you can use Basic code to set or clear a bit within a variable, BitSet0 is more convenient, more

readable, and also allows you set the high bit (bit 15, or sign bit) of an integer.

To set a bit In Visual Basic:

   Flags% = Flags% Or 2 ^ Bit%

To clear a bit in Visual Basic:

    Flags% = Flags% And Not 2 ^ Bit%

vs:

    PrevVal% = BitSet0%(Flags, Bit%, NewVal%)

or simply:

    BitSet0 Flags, Bit%, NewVal%

To later test a bit's value, use the companion BitGet0 function.

Another advantage to this routine is that non integer variable types can be used to hold the flag values

such as floating point numbers, and strings.  Note that if you use a string, you must pass it by value as

follows:

   BitSet0%(ByVal Flags$, Bit%, NewVal%)

See Also:

BitGet0, BitPack0, BitUnPack0


BitUnPackO
BitUnPack0

assembler function contained in QPRO200.DLL

Purpose:

BitUnPack0 retrieves the value of a specified number of bits within a variable.

Declare Syntax:

Declare Function BitUnPack0% Lib "QPRO200.DLL" (Flags As Any, ByVal StartBit%, _

   ByVal NumBits%)

Call Syntax:

Value% = BitUnPack0%(Flags, StartBit%, NumBits%)

for strings:

Value% = BitUnPack0%(ByVal Flags$, StartBit%, NumBits%)

Where:

Flags is a block of data such as a numeric variable, or fixed length string of up to 8192 bytes (65536

bits).  Note that Variants cannot be used.  StartBit% is the first bit to get, NumBits% is the number of bits

to get.  Note that the first bit position is specified as 0.  Value% receives the value of the specified bits.

Comments:

See the comments sections of the BitPack0, and BitSet0 routines elsewhere in this manual for a

discussion of manipulating bits.

The example for the BitPack0 routine shows how to create a variable whose first 3 bits represent 3 flag

values, and the next 8 hold a number.  The following example shows how to unpack this data from the

variable that holds it.

   '--- Extract the first 3 bits into a variable

   StartBit% = 0                  'Start at the first bit

   NumBits% = 3                   'Get 3 bits at once

   Bits% = BitUnPack0%(Flags%, StartBit%, NumBits%)

   '--- Test the individual bits

   If BitGet0(Bits%, 0) Then ...   'Test first bit

   If BitGet0(Bits%, 2) Then ...   'Test second, etc.

   '--- Now get the value of the next 8 bits

   StartBit% = 3                   'Start at the forth bit

   NumBits% = 8                    'Get 8 bits at once

   Num% = BitUnPack0%(Flags%, StartBit%, NumBits%)

Note that you can also use non integer variable types to store greater quantities of information than can

be packed into either an integer, or long integer such as floating point numbers, and strings.  Note

however that if you use a string, you must pass it by value as follows:

   BitSet0%(ByVal Flags$, Bit%, NewVal%)

See Also:

BitGet0, BitPack0, BitSet0


Compare

assembler function contained in QPRO200.DLL

Purpose:

Compare will compare any two blocks of memory, and report if they are the same.

Declare Syntax:

Declare Function Compare% Lib "QPRO200.DLL" (ByVal Seg1%, ByVal _

   Adr1%, ByVal Seg2%, ByVal Adr2%, ByVal NumBytes%)

Call Syntax:

Same% = Compare%(Seg1%, Adr1%, Seg2%, Adr2%, NumBytes%)

Where:

Seg1% and Adr1% tell where the first block is located, and Seg2% and Adr2% point to the second.

NumBytes% is either the number of bytes to be compared, or a special code indicating a type of variable

(see below). Same% then receives -1 if the two blocks are identical, or 0 if they are not.

Comments:

Not unlike AddUSI, Compare was designed to serve a very special, but important purpose. Because the

KeySort subprogram must be able to work with any arrangement of a Type array, it is mandatory that the

element comparisons be performed by examining memory. For example, we couldn't use something like:

   If Array(X).StringPart = Array(X + 1).StringPart

The whole point of a Type array is that the programmer can decide how it is to be organized. But Visual

Basic doesn't provide a way to get at individual members of a Type variable or array element, other than

by specifying its name. Thus, Compare provides an easy solution to the problem.

However, there are no doubt other, similar situations where Compare could be useful. For example, to

see if a block of elements in one array is the same as those in a second one.

NumBytes% may also be coded using the same system as the QuickPak Professional for Windows sort

routines:

    -1        2-byte integer

    -2        4-byte long integer        }these are the same and

    -3        4-byte single precision    }are interchangeable

    -4        8-byte double precision

    -5        8-byte currency

    +n        n-byte fixed-length string or any block of data

See Also:

CompareT


CompareT

assembler function contained in QPRO200.DLL

Purpose:

CompareT will compare any two Type variables, and report if they are the same.

Declare Syntax:

Declare Function CompareT% Lib "QPRO200.DLL" (Type1 As Any, Type2 As Any, _

   ByVal NumBytes%)

Call Syntax:

Same% = CompareT%(Type1, Type2, NumBytes%)

Where:

Type1 and Type2 are two Type variables or elements in a Type array, and NumBytes% indicates their

length in bytes. Same% then receives either -1 if they are the same, or zero if they are not.

Comments:

Even though BASIC will allow you to assign one Type variable to another it is not possible to simply

compare them. For example, either of the statements below will result in an error message:

   If Type1 = Type2 Then . . .

or

   If Type1 <<>> Type2 Then . . .

Before CompareT, the only solution was to compare each component of the Type individually:

  If Typ1.A = Typ2.A And Typ1.B = Typ2.B And Typ1.C = Typ2.C And...

This quickly becomes tedious when there are many components to each Type variable. Many individual

comparisons also create a lot of wasted code that takes an undue amount of time to execute.

CompareT is modeled after the more generalized Compare routine, but it is designed to be simpler to

use when comparing two Type variables.

Example Location:

Project File   	Form/BASIC File	Procedure

FILEMOD.MAK	FCOMPBAS.FRM	FileComp


CSMaximum

assembler function contained in QPRO200.DLL

Purpose:

CSMaximum compares two numeric variables of any type and returns the larger of the two.

Declare Syntax:

Declare Function CSMaximum Lib "QPRO200.DLL" (Value1 As Variant, Value2 As Variant) As Variant

Call Syntax:

Higher = CSMaximum(Value1, Value2)

Where:

Value1 and Value2 are the two values being considered, and Higher receives the greater of the two

values.

Comments:

These routines provide a convenient way to avoid code such as:

   If X > Y Then

      A = X

   Else

      A = Y

   End If

A single assignment may be used instead:

     A = CSMaximum(X, Y)

This routine is similar to the MaxInt, and MaxLong routines except that it can accept and return any

numeric data type.  If however you need to compare only integers, or long integers, you should use

either MaxInt, or MaxLong instead as they are usually faster.

See Also:

CSMinimum, MaxInt, MinInt, MaxLong, MinLong


CSMinimum

assembler function contained in QPRO200.DLL

Purpose:

CSMinimum compares two numeric variables of any type and returns the lesser of the two.

Declare Syntax:

Declare Function CSMinimum Lib "QPRO200.DLL" (Value1 As Variant, Value2 As Variant)_

  As Variant

Call Syntax:

Lower = CSMinimum(Value1, Value2)

Where:

Value1 and Value2 are the two values being considered, and Lower receives the lesser of the two

values.

Comments:

These routines provide a convenient way to avoid code such as:

    If X < Y Then

       A = X

    Else

       A = Y

    End If

A single assignment may be used instead:

  A = CSMinimum(X, Y)

This routine is similar to the MinInt, and MinLong routines except that it can accept and return any

numeric data type.  If however you need to compare only integers, or long integers, you should use

either MinInt, or MinLong instead as they are usually faster.

See Also:

CSMaximum, MaxInt, MinInt, MaxLong, MinLong


CvC,D,I,L,S

assembler functions contained in QPRO200.DLL

Purpose:

CvC@, CvD#, CvI%, CvL&, and CvS! return numbers of a certain precision given a string containing the

IEEE representation of the number.  Five separate functions are provided, with one each intended for

currency, double precision, integer, long integer, and single precision data types.

Declare Syntax:

Declare Function CVC@ Lib "QPRO200.DLL" (Number$)

Declare Function CVD# Lib "QPRO200.DLL" (Number$)

Declare Function CVI% Lib "QPRO200.DLL" (Number$)

Declare Function CVL& Lib "QPRO200.DLL" (Number$)

Declare Function CVS! Lib "QPRO200.DLL" (Number$)

Call Syntax:

Num% = CVI%(Number$)

Num& = CVL&(Number$)

Num! = CVS!(Number$)

Num# = CVD#(Number$)

Num@ = CVC@(Number$)

Where:

Num? receives the value represented by the IEEE string held in Number$.

Comments:

These functions are replacements for the DOS BASIC versions of the same names, which are not

supported by Visual Basic.  Among other things, they provide a method for accessing a random access

file buffer without requiring a type structure.  This lets you access any record structure at runtime as

shown below, rather than defining a static structure in code:

   Buf$ = Space$(6)

   Get Handle, RecNo, Buf$

   iNum% = CvI%(Mid$(Buf$, 1, 2))

   sNum! = CvS!(Mid$(Buf$, 3, 4))

See Also:

The CvxMbf, Mkx$ and MkxMbf$ families of functions.


CvDMbf and CvSMbf

assembler functions contained in QPRO200.DLL

Purpose:

CvSMbf and CvDMbf return single or double precision numbers, respectively, given a Microsoft Binary

Format (MBF) string representation of a number.

Declare Syntax:

Declare Function CvDMbf# Lib "QPRO200.DLL" (Number$)

Declare Function CvSMbf! Lib "QPRO200.DLL" (Number$)

Call Syntax:

Num# = CvDMbf#(Number$)

Num! = CvSMbf!(Number$)

Where:

Num! receives the value represented by a four-byte Microsoft Binary Format string (Number$), and

Num# receives a value from an eight-byte MBF string.

Comments:

These functions are replacements for the DOS BASIC versions of the same names, which are not

supported by Visual Basic.  This lets a program access data files that were originally created with early

versions Microsoft BASIC.

See Also:

The Cvx, Mkx$ and MkxMbf$ families of functions.


Date2Day

Visual Basic function contained in DATE2DAY.BAS

Purpose:

Date2Day accepts an incoming date string, and returns the appropriate day of the week (1 - 7).

Syntax:

Day = Date2Day%(D$)

Where:

D$ is a string such as "MMDDYY", "MM-DD-YYYY", and so forth, and Day receives a value

corresponding to the day of the week.

See Also:

The related function Num2Day instead accepts the incoming date as an integer.

Example Location:

Project File	Form/BASIC File	Procedure

DATETIME.MAK	DATETIME.FRM	GetDate


Date2Num

assembler function contained in QPRO200.DLL

Purpose:

Date2Num converts a date in string form to an equivalent integer variable.

Declare Syntax:

Declare Function Date2Num% Lib "QPRO200.DLL" (ByVal Dat$)

Call Syntax:

Num% = Date2Num%(D$)

Where:

D$ is a date in the form of "MMDDYY" or "MM-DD-YY" or "MM/DD/YYYY", or any such combination,

and Num% receives the number of days before or after 12-31-1979.

Comments:

Date2Num is a very powerful routine with two important uses. Besides allowing what would otherwise be

an eight character string to be packed to only two bytes, it also provides an easy way to perform date

arithmetic.

Date2Num will operate on any date that is within the range 01-01-1900 to 11-17-2065. Invalid dates

and dates that fall outside of that range will return -32768 to indicate an error.

Once a date has been converted to the equivalent integer value, you may add or subtract a number of

days, and then use the companion function Num2Date to convert the result. The example below shows

this in context:

   D$ = "09-17-88"

   Start% = Date2Num%(D$)

   Later% = Start% + 30

   After30$ = Num2Date$(Later%)

   Print "Thirty days after "; D$; " is "; After30$

Because Date2Num and Num2Date are set up as functions they may also be used within a print

statement directly, along with optional calculations:

   Print "30 days after"; D$; " is " Num2Date$(Start% + 30)

Date2Num and Num2Date are also useful for verifying if a given date is valid, which eliminates tedious

calculations that you would have to perform to take possible leap years into consideration.

The only requirement for the date validation example below is that the original date must be in the form

"MM-DD-YYYY", because this is the format returned by Num2Date.

   D$ = CtrlName.Text

   Dat% = Date2Num%(D$)

   If Num2Date$(Dat%) = D$ Then

      Print D$; " is a good date!"

   Else

      Print "Please try again."

   End If

What we are doing here is asking for an original date, and then converting it to an equivalent number. If

after converting it back to a string again we have the same date that we started with, then the date was

valid.

Understand that while days before 12-31-1979 are returned by Date2Num as negative values, adding

and subtracting will still be performed correctly.

See Also:

Num2Date

Example Location:

Project File	Form/BASIC File	Procedure

DATETIME.MAK	DATETIME.FRM	FmtDate


DayName

assembler function contained in QPRO200.DLL

Purpose:

DayName accepts an integer value between 1 and 7, and returns an equivalent day name as a string in

the form "Sun", "Mon", "Tue", and so forth.

Declare Syntax:

Declare Function DayName$ Lib "QPRO200.DLL" (ByVal Day%)

Call Syntax:

D$ = DayName$(Day%)

Where:

Day% is an integer within the range of 1 and 7 inclusive, and D$ receives the equivalent day name in the

form of a string.

Comments:

Because DayName is a function it may be used directly in a Print or assignment statement, or in

combination with other Visual Basic or QuickPak Professional for Windows functions:

   Print "Today is " DayName$(Day%)

or

   Today$ = DayName$(Date2Day%(Date$))

See Also:

MonthName

Example Location:

Project File	Form/BASIC File	Procedure

DATETIME.MAK	DATETIME.FRM	GetDate


EDate2Num

assembler function contained in QPRO200.DLL

Purpose:

EDate2Num accepts a date in the European "DDMMYY" format, and returns a corresponding integer

value.

Declare Syntax:

Declare Function EDate2Num% Lib "QPRO200.DLL" (ByVal Dat$)

Call Syntax:

Days% = EDate2Num%(Dat$)

Where:

Dat$ is a date in the form of "DDMMYY" or "DD-MM-YYYY" or "DD.MM.YYYY" or any such combination,

and Days% receives the number of days before or after 31-Dec-1979 (12-31-1979).

Comments:

EDate2Num is nearly identical to the Date2Num routine meant for use with American dates, but instead

accepts dates in the European format.  See the description for Date2Num for more information about

storing dates as integers.

See Also:

ENum2Date

Example Location:

Project File	Form/BASIC File	Procedure

DATETIME.MAK	DATETIME.FRM	CheckDate


ENum2Date

assembler function contained in QPRO200.DLL

Purpose:

ENum2Date converts a previously encoded integer date to an equivalent string in the European format.

Declare Syntax:

Declare Function ENum2Date$ Lib "QPRO200.DLL" (ByVal Days%)

Call Syntax:

Dat$ = ENum2Date$(Days%)

Where:

Days% is an integer variable or value within the range -29219 to 31368, and Dat$ receives the date in

the form of "DD-MM-YYYY".

Comments:

ENum2Date is nearly identical to the Num2Date routine meant for use with American dates, but instead

returns dates in the European format. See the description for Date2Num for more information about

storing dates as integers.

See Also:

EDate2Num

Example Location:

Project File	Form/BASIC File	Procedure

DATETIME.MAK	DATETIME.FRM	CheckDate


Evaluate

Visual Basic function contained in EVALUATE.BAS

Purpose:

Evaluate is a full-featured expression evaluator. It accepts a formula in an incoming string, and returns a double precision result. Capitalization is ignored (in keywords such as LOG and SIN), except for the "E" used for scientific notation. To Evaluate, a lower case "e" represents the constant, and an upper case "E" is for the exponent.

Syntax:

Answer# = Evaluate#(Expression$)

Where:

Expression$ is of the form

    10 * (12 ^ 3 + (4E-13)) /

    LOG(8)

and Answer# receives the computed answer. If the incoming string is invalid (for example a mismatched number of parentheses), the incoming string will be returned with a leading percent sign (%) appended to it.

Comments:

Scientific notation is also supported using "E" or "D" (but not "e").

For example: 10E+3 or 5E-19

Evaluate recognizes two constants, "PI" and "e".

The table below shows the operations supported by Evaluate:

   ABS	Absolute Value

   AND	Logical AND

   ARCCOS	Arc Cosine

   ARCCOSH	Arc Hyperbolic Cosine

   ARCCOT	Arc Cotangent

   ARCCOTH	Arc Hyperbolic Cotangent

   ARCCSC	Arch Cosecant

   ARCCSCH	Arc Hyperbolic Cosecant

   ARCTANH	Arc Hyperbolic Tangent

   ARCSEC	Arc Secant

   ARCSECH	Arc Hyperbolic Secant

   ARCSIN	Arc Sine

   ARCSINH	Arc Hyperbolic Sine

   ATN	Arc Tangent

   CLG	Common Log (base 10, what LOG really is)

   COS	Cosine

   COT	Cotangent

   COTH	Hyperbolic Cotangent

   CSC 	Cosecant

   CSCH	Hyperbolic Cosecant

   EXP	Exp

   LOG	Natural Log (base e, hwat Visual Basic calls LOG)

   NOT	Logical NOT

   OR	Logical OR

   SINH	Hyperbolic Sine

   SECH	Hyperbolic Secant

   SEC	Secant

   SIN	Sine

   SQR	Square Root

   TAN	Tangent

   TANH	Hyperbolic Tangent

The following table shows the math operators supported by Evaluate.

   !                Factorial

  ^                 Exponential

   *                Multiplication

   /                Division

   \                Integer Division

   +                Addition

   -                Subtraction (or unary minus such as -15)

   <                Greater than

   =                Equal to

   >                Less than

Example Location:

Project File	Form/BASIC File	Procedure

EVALUATE.MAK	EVALUATE.FRM	Command1_Click


Factorial

assembler function contained in QPRO200.DLL

Purpose:

Factorial provides an extremely fast way to obtain a factorial value.

Declare Syntax:

Declare Function Factorial# Lib "QPRO200.DLL" (ByVal Number%)

Call Syntax:

Answer# = Factorial#(Number%)

Where:

Number% is a number between 0 and 170, and Answer receives its factorial. If answer is negative or

greater than 170, Answer instead receives -1.

Comments:

Computing factorials takes an enormous amount of time because so many double precision numbers

must be multiplied. The approach we have taken here is to instead calculate all of the answers ahead of

time, and place them into a table. This way, Factorial can look up the correct answer very quickly.


Fre

assembler function contained in QPRO200.DLL

Purpose:

Fre returns either the amount of free EMS memory, or the total amount of available (real and disk-based

protected mode) memory.

Declare Syntax:

Declare Function Fre& Lib "QPRO200.DLL" (ByVal Flag%)

Call Syntax:

GlobalMemory& = Fre&(Flag%)

Where:

Flag can be either -1 or -2.  A value of -1 specifies that all available memory should be returned, and -2

is used to specify available EMS memory.

The value returned is a long integer.

Comments:

This function is a replacement for DOS BASIC's FRE() function which is not supported by Visual Basic.

You can use the returned value to determine if there is enough memory available to dimension a

particular array, or perform an operation.

See Also:

GetSystemResources%

Example Location:

Project File	Form/BASIC File	Procedure

SYSINFO.MAK	SYSINFO.FRM	Form_Load


GetCPU

assembler function contained in QPRO200.DLL

Purpose:

GetCPU returns an integer value that indicates the type of CPU installed in the host PC.

Declare Syntax:

Declare Function GetCPU% Lib "QPRO200.DLL" ()

Call Syntax:

CPU% = GetCPU%()

Where:

CPU receives either 86, 286, or 386 to indicate the presence of an 8086/88 (or NEC V20/30), 80286, or

80386 CPU respectively.

Comments:

The value of a routine such as GetCPU is dubious as best, but our customers have asked for it so here it

is. One possible application might be to estimate how long an operation will take, perhaps for timing

delays.

See Also:

MathChip


GetEquip

assembler subroutine contained in QPRO200.DLL

Purpose:

GetEquip returns several items from the equipment list kept in the low-memory area of a PC.

Declare Syntax:

Declare Sub GetEquip Lib "QPRO200.DLL" (Floppies%, Parallels%, Serials%)

Call Syntax:

GetEquip Floppies%, Parallels%, Serials%

Where:

Floppies% returns holding the number of floppy disks, Parallels% holds the number of parallel printer

adapters, and Serials% tells how many serial ports are installed.

Comments:

GetEquip is useful in a variety of situations. It is particularly handy when used in conjunction with an

automatic installation program for applications you create. For example, knowing that a PC has only one

floppy disk drive helps you avoid the "Insert disk for drive B" nuisance message that DOS displays.

Likewise, if a given PC has only one parallel or serial port, then you could avoid a potential lockup

caused by trying to access a device that isn't there.


HiWord

assembler function contained in QPRO200.DLL

Purpose:

Returns the high order integer portion of a long integer.

Declare Syntax:

Declare Function HiWord% Lib "QPRO200.DLL" (ByVal LongInt&)

Call Syntax:

HighWord% = HiWord%(LongInt&)

Where:

LongInt& is the source variable.

Returns:

HighWord% receives the value of the high two bytes of the source variable LongInt&.

Comments:

There are many situations where two integer values are represented by a single long integer variable.

This function makes it simple to extract the integer contained in the high portion of the long integer.  This

equivalent to the following Basic code:

    HighWord% = LongInt& \ 65536

See Also:

LoWord, and MakeLong


Inp

assembler function contained in QPRO200.DLL

Purpose:

Inp serves the same purpose as the DOS BASIC function of the same name, and it is used for reading a

port.

Declare Syntax:

Declare Function Inp% Lib "QPRO200.DLL" (ByVal PortNo%)

Call Syntax:

Byte% = Inp%(PortNo%)

Where:

PortNo% is the port number to read, and Word receives an integer containing the data at that port.

Comments:

This routine lets you read data from a given machine port, which Visual Basic does not allow.

See Also:

Out


Inp2

assembler function contained in QPRO200.DLL

Purpose:

Inp2 serves the same purpose as the DOS BASIC function of the same name, and is used for reading

two bytes from a hardware port.

Declare Syntax:

Declare Function Inp2% Lib "QPRO200.DLL" (ByVal PortNo%)

Call Syntax:

Word% = Inp2%(PortNo%)

Where:

PortNo% is the machine port number to read, and Word% receives an integer containing two bytes of

data from that port.

Comments:

While the Inp routine reads a single byte of data from a port, this routine will read a two byte word.

Also See:

Inp, Out and Out2


LoWord

assembler function contained in QPRO200.DLL

Purpose:

Returns the low order integer portion of a long integer.

Declare Syntax:

Declare Function LoWord% Lib "QPRO200.DLL" (ByVal LongInt&)

Call Syntax:

LowWord% = LoWord%(LongInt&)

Where:

LongInt& is the source variable.

Returns:

LowWord% receives the value of the low two bytes of the source variable LongInt&.

Comments:

There are many situations where two integer values are represented by a single long integer variable.

This function makes it simple to extract the integer contained in the low portion of the long integer.  This

equivalent to the following Basic code:

   LowWord% = LongInt& Mod 65536

See Also:

HiWord, and MakeLong


LPStr

assembler function contained in QPRO200.DLL

Purpose:

Returns a long integer (far) pointer to a Visual Basic High Level Language (VBHLL) string.

Declare Syntax:

Declare Function LpStr& Lib "QPRO200.DLL" (Text$)

Call Syntax:

LpStrData& = LPStr&(Text$)

Where:

The function returns a far (segment and offset) pointer to the data contained in Text$.

Comments:

You can use this function to find the address of a string so that it can be passed on to a Windows API, or C routine that expects a far pointer of this type.  While you can specify the ByVal keyword for string parameters in function calls, some functions require strings to be specified with a long pointer, in a Type structure.

Note that when passing the address of a string in this way, it is up to you to add a Chr$(0) to the end of the string if this is expected by the called routine.

See Also:

QPSegAdr, SAdd, SSeg, VarPtr, VarSeg


MakeLong

assembler function contained in QPRO200.DLL

Purpose:

Returns a long integer combination of two integers.

Declare Syntax:

Declare Function MakeLong& Lib "QPRO200.DLL" (ByVal LowWord%, ByVal HighWord%)

Call Syntax:

LongInt& = MakeLong&(LowWord%, HighWord%)

Where:

LowWord% will be stored in the long integer's low portion, and HighWord% will be stored in the upper

portion.

Returns:

LongInt& receives the combination of the two integer parameters.

Comments:

There are many situations where two integer values are represented by a single long integer variable,

and MakeLong makes it simple to combine them.   This function is equivalent to the following Basic

code:

   LongInt& = 65536 * HighWord% + LowWord%

See Also:

HiWord, and LoWord


MathChip

assembler function contained in QPRO200.DLL

Purpose:

MathChip will report if an 80x87 math co-processor chip is installed in the host PC.

Declare Syntax:

Declare Function MathChip% Lib "QPRO200.DLL" ()

Call Syntax:

There% = MathChip%()

Where:

There% receives either -1 if a coprocessor is installed, or 0 if one is not.

Comments:

MathChip is furnished as a companion to the GetCPU function, to provide information about the host

computer.

See Also:

GetCPU


MaxInt and MaxLong

assembler functions contained in QPRO200.DLL

Purpose:

MaxInt compares two integer variables, and returns the value of the higher one. MaxLong is similar, but

is intended for use with long integers.

Declare Syntax:

Declare Function MaxInt% Lib "QPRO200.DLL" (ByVal value1%, ByVal value2%)

Declare Function MaxLong& Lib "QPRO200.DLL" (ByVal value1&, ByVal value2&)

Call Syntax:

Higher% = MaxInt%/(Value1%, Value2%)

or

Higher& = MaxLong&/(Value1&, Value2&)

Where:

Value1x and Value2x are the two values being considered, and Higherx receives the greater of the two

values.

Comments:

These routines provide a convenient way to avoid code such as:

   If X% >> Y% Then

      A% = X%

   Else

      A% = Y%

   End If

A single assignment may be used instead:

 A% = MaxInt%(X%, Y%)

See Also:

MinInt and MinLong

Example Location:

Project File	Form/BASIC File	Procedure

EVALUATE.MAK	EVALUATE.FRM	Evaluate


MinInt and MinLong

assembler functions contained in QPRO200.DLL

Purpose:

MinInt compares two integer variables, and returns the value of the smaller one. MinLong is similar, but

is intended for use with long integers.

Declare Syntax:

Declare Function MinInt% Lib "QPRO200.DLL" (ByVal Value1%, ByVal Value2%)

Declare Function MinLong& Lib "QPRO200.DLL" (ByVal Value1&, ByVal Value2&)

Call Syntax:

Lower% = MinInt%/(Value1%, Value2%)

or

Lower& = MinLong&/(Value1&, Value2&)

Where:

Value1x and Value2x are the two values being considered, and Lowerx receives the lesser of the two

values.

Comments:

These functions provide a convenient way to avoid code such as:

   If X% << Y% Then

      A% = X%

   Else

      A% = Y%

   End If

A single assignment may be used instead:

   A% = MinInt%(X%, Y%)

See Also:

MaxInt and MaxLong

Example Location:

Project File	Form/BASIC File	Procedure

EVALUATE.MAK	EVALUATE.BAS	Evaluate


MkC,D,I,L,S

assembler functions contained in QPRO200.DLL

Purpose:

MkC$, MkD$, MkI$, MkL$, and MkS$ return a string containing the IEEE representation of a number.

Five separate functions are provided, with one each intended for currency, double precision, integer,

long integer, and single precision data types.

Declare Syntax:

Declare Function MKC$ Lib "QPRO200.DLL" (ByVal Number@)

Declare Function MKD$ Lib "QPRO200.DLL" (ByVal Number#)

Declare Function MKI$ Lib "QPRO200.DLL" (ByVal Number%)

Declare Function MKL$ Lib "QPRO200.DLL" (ByVal Number&)

Declare Function MKS$ Lib "QPRO200.DLL" (ByVal Number!)

Call Syntax:

Num$ = MKC$(Number@)

Num$ = MKD$(Number#)

Num$ = MKI$(Number%)

Num$ = MKL$(Number&)

Num$ = MKS$(Number!)

Where:

Num$ receives the IEEE representation of Numberx.

Comments:

These functions are replacements for the DOS BASIC versions of the same names, which are not

supported at this time under Visual Basic.  Among other things, they provide a method for preparing a

random access file buffer that does not require a type structure.

    Buf$ = Space$(6)

    Mid$(Buf$, 1) = MkI$(iNum%)

    Mid$(Buf$, 3) = MkS$(sNum!)

    Put Handle, RecNo, Buf$

This makes it possible to define a record structure at runtime rather than defining a static structure in

code.

See Also:

CvX, CvXMbf and MkXMbf$.


MkSMbf and MkDMbf

assembler functions contained in QPRO200.DLL

Purpose:

MkSMbf and MkDMbf return Microsoft Binary Format (MBF) strings given single or double precision

numbers respectively.

Declare Syntax:

Declare Function MkDMbf$ Lib "QPRO200.DLL" (ByVal Number#)

Declare Function MkSMbf$ Lib "QPRO200.DLL" (ByVal Number!)

Call Syntax:

Num$ = MkDMbf$(Number#)

Num$ = MkSMbf$(Number!)

Where:

Num$ receives either a four- or eight-byte Microsoft Binary Format string representing the value of either

Number! or Number#.

Comments:

These functions are replacements for the DOS BASIC versions of the same names, which are not

supported by Visual Basic.  This lets a program Process data files that were originally created with early

versions Microsoft BASIC.

See Also:

CvX, CvXMbf, and MkX$.


MonthName

assembler function contained in QPRO200.DLL

Purpose:

MonthName accepts an integer value between 1 and 12, and returns an equivalent month name as a

string in the form "Jan", "Feb", "Mar", and so forth.

Declare Syntax:

Declare Function MonthName$ Lib "QPRO200.DLL" (ByVal Month%)

Call Syntax:

M$ = MonthName$(Month%)

Where:

Month% is an integer within the range of 1 and 12 inclusive, and M$ receives the equivalent month name

in the form of a string.

Comments:

Because MonthName is a function it may be used directly in a Print or assignment statement, or in

combination with other Visual Basic or QuickPak Professional for Windows functions:

   Print "The current month is " MonthName$(Month%)

or

   Month$ = MonthName$(Val(Left$(Date$, 2)))

See Also:

DayName

Example Location:

Project File	Form/BASIC File	Procedure

DATETIME.MAK	DATETIME.FRM	GetDate


NetName

assembler function contained in QPRO200.DLL

Purpose:

NetName returns a local network machine name if your program is running on a Novell compatible

network.

Declare Syntax:

Declare Function NetName$ Lib "QPRO200.DLL" ()

Call Syntax:

NetID$ = NetName$()

Where:

NetID$ receives the network ID of the local machine on which the program is running.

Comments:

There are several situations where you might want to use a unique name that identifies a particular

network machine.  Many database programs use this to stamp database records with the name of the

user who entered the data for later reference.  A network name is also useful for providing limited access

to certain databases or records given the users name.

Example Location:

Project File	Form/BASIC File	Procedure

SYSINFO.MAK	SYSINFO.FRM	Form_Load


NetType

assembler function contained in QPRO200.DLL

Purpose:

NetType returns the type of network installed.

Declare Syntax:

Declare Function NetType% Lib "QPRO200.DLL" ()

Call Syntax:

Network% = NetType

Where:

NetType returns with one of the following values:

   5 = Topware

   4 = DECNet

   3 = Banyan Vines

   2 = Microsoft or Lantastic

   1 = Novell

   0 = None

See Also:

NetTypeName


NetTypeName

assembler function contained in QPRO200.DLL

Purpose:

NetTypeName returns the name of the network type installed.

Declare Syntax:

Declare Function NetTypeName$ Lib "QPRO200.DLL"

Call Syntax:

Network$ = NetTypeName$()

Where:

NetTypeName$ returns as one of the following strings:

   None

   Novell

   Microsoft or Lantastic

   Banyan Vines

   DECNet

   Topware

See Also:

NetType


Num2Date

assembler function contained in QPRO200.DLL

Purpose:

Num2Date converts a previously encoded integer date to an equivalent date string.

Declare Syntax:

Declare Function Num2Date$ Lib "QPRO200.DLL" (ByVal Days%)

Call Syntax:

D$ = Num2Date$(Days%)

Where:

Days% is an integer variable or value within the range -29219 to 31368, and D$ receives the date in the

form "MM-DD-YYYY".

Comments:

A complete discussion of the QuickPak Professional for Windows date conversion method is given in the

section that describes the Date2Num routine.


Num2Day

assembler function contained in QPRO200.DLL

Purpose:

Num2Day accepts an integer number that represents a date in the QuickPak Professional for Windows

format, and returns the appropriate day of the week (1 - 7).

Declare Syntax:

Declare Function Num2Day% Lib "QPRO200.DLL" (ByVal D%)

Call Syntax:

Day% = Num2Day%(D%)

Where:

D% is a date that has already been converted to the equivalent integer format, and Day receives a value

corresponding to the day of the week.

Comments:

Num2Day is useful in conjunction with the DayName$ function, as shown in the DATETIME.MAK

example program.

See Also:

Also see the related Visual Basic function Date2Day, that instead accepts the date as a string.


Num2Time

assembler function contained in QPRO200.DLL

Purpose:

Num2Time converts a long integer that represents the number of seconds past midnight to an equivalent

time in string from.

Declare Syntax:

Declare Function Num2Time$ Lib "QPRO200.DLL" (Time&)

Call Syntax:

T$ = Num2Time$(Time&)

Where:

Time& is a long integer variable or value within the range 0 to 86400, and T$ receives the time in the

form "HH:MM:SS".

Comments:

A complete discussion of the QuickPak Professional for Windows time conversion method is given in the

section that describes the Time2Num routine.

Example Location:

Project File	Form/BASIC File	Procedure

DATETIME.MAK	DATETIME.FRM	AgePB_Click


Out

assembler subroutine contained in QPRO200.DLL

Purpose:

Out is a replacement for the routine of the same name in DOS BASIC, and is are used to write directly to

a machine port.

Declare Syntax:

Declare Sub Out Lib "QPRO200.DLL" (ByVal PortNo%, ByVal Byte%)

Call Syntax:

Out PortNo%, Byte%

Where:

PortNo% is the port number, and Word% is an integer containing the data to be written.

Comments:

These routines allow you to write data to a hardware port, which Visual Basic does not allow.

See Also:

Inp & Inp2


Out2

assembler subroutine contained in QPRO200.DLL

Purpose:

Out2 serves the same purpose as the DOS BASIC function of the same name, and is used for writing

two bytes to a hardware port.

Declare Syntax:

Declare Sub Out2 Lib "QPRO200.DLL" (ByVal PortNo%, ByVal Word%)

Call Syntax:

Out2 PortNo%, Word%

Where:

PortNo% is the machine port number to write to, and Word% is an integer containing two bytes of data to

write.

Comments:

While the Out routine writes a single byte of data to a port, this routine will write a two byte word.

Also See:

Inp, Inp2 and Out


Peek1

assembler function contained in QPRO200.DLL

Purpose:

Peek1 reads a byte from a specified segment and address.

Declare Syntax:

Declare Function Peek1% Lib "QPRO200.DLL" (ByVal Segment%, ByVal Address%)

Call Syntax:

Byte% = Peek1%(Segment%, Address%)

Where:

Segment% and Address% indicate where the byte to be read is located, and Byte% receives its value.

Comments:

Remember that Windows can and does move items in memory without your knowledge.  It is a good

idea to obtain the segment and address of the memory area in question each time you read from that

location.


Peek2

assembler function contained in QPRO200.DLL

Purpose:

Peek2 reads a word (two bytes) from a specified segment and address.

Declare Syntax:

Declare Function Peek2% Lib "QPRO200.DLL" (ByVal Segment%, ByVal Address%)

Call Syntax:

Word% = Peek2%(Segment%, Address%)

Where:

Segment% and Address% indicate where the word to be read is located, and Word% receives its value.

Comments:

Remember that Windows can and does move items in memory without your knowledge.  It is a good

idea to obtain the segment and address of the memory area in question each time you read from that

location.


Play

assembler function contained in QPRO200.DLL

Purpose:

Plays music according to a script string using the Windows sound driver.

Declare Syntax:

Declare Function Play% LIB "QPRO200.DLL" (Tune$)

Call Syntax:

Status% = Play%(Tune$)

Where:

Tune$ is a string containing a music script to play, and Status% receives the return status of the function

as follows:

Value:	Explanation:

-1	Play was successful.  There should also be sound  emanating from the PC.

  0	A previous Play statement is executing in the background.

+1	The Windows sound driver SOUND.DRV is not available.

+2	The sound driver cannot allocate a buffer large enough to hold the contents of Tune$.

Comments:

This function requires that the Windows sound driver SOUND.DRV supplied with QuickPak Professional

for Windows be installed in your Windows system directory.  The driver supplied with this package is an

updated version of the one supplied with Windows 3.0, and you may distribute it with your applications.

Since the Windows sound driver cannot accept a new Play string when it is busy executing a previous

script, you must create code to check the driver's status as follows:

    Do Until Play%(Tune$)

       Dummy% = DoEvents%()

    Loop

This example continually tries to execute the script contained in Tune$ until the Play function returns a

non-zero result.  A return status other than zero indicates that the music was accepted by the sound

driver, or an error occurred preventing any music from being played.  The DoEvents%() statement

ensures that other Windows tasks are able to execute while you are waiting for the sound driver to

complete the script.

It is also extremely important that you close the sound driver when you are done using the Play function.

To do this, simply call it with a null string as follows:

    Status% = Play%("")

This interrupts any sound currently being played and closes the sound driver.  If you need to wait for a

currently playing tune to finish, you can use code such as the following:

    Do Until Play%("X")

       Dummy% = DoEvents%()

    Loop

    Status% = Play%("")

The Do loop above waits for any music that is currently playing to finish, by continually sending an illegal

string to Play.  When the sound driver becomes available the loop is exited, and the driver is closed by

playing a null string.

Again, you must close the sound driver when you are done playing notes.  If you end your program

without doing so, the sound driver will not be available to any other tasks including subsequent

invocations of your program.

Play uses the same script strings supported by the PLAY statement in regular Microsoft BASIC.

Play supports the following basic notation:

   Letter notes "A" through "G" in upper or lower case

   Optional "+" or "#" suffix to indicate a musical Sharp

   Optional "-" suffix to indicate a musical Flat

   Optional numeric suffix to override a note's duration

      Example: "c#16 g- c"

   Tempo in the range of 32 through 255

      Example: "t155 bbb8 bbb8"

      Default: "t120"

   Length of each note in the range of 1 through 64

      Example: "L16 bbb8 bbb8"

      Default: "L4"

   Octave in the range of 0 through 6

      Example: "o2 ggg P32 e-4"

      Default: "o4"

   Octave Up and Down ">>" and "<<" symbols

      Example: "cdefgab>>c<<bagfedc"

   Music Normal, Staccato, Legato modifiers

      Example: "mn c ms d ml l32ef"

      Default: "mn"

      Note: BASIC's "MF" and "MB" have no effect and are ignored.

   Pause (rest) for a duration 1 through 64

      Example: "l8 ggg p32 e-4"

   Numeric notes in the range of 0 through 84

      Example: "n55 n56 n57 n0 n58"

      Note that a note value of zero ("n0") is a rest.

The following statements play "On top of old smokey".  Note that these multiple statements should be

combined into a single long string for code efficiency.  This example is shown in portions for ease of

commenting. Also note that extra spaces are ignored by the Play function and are used here solely for

clarity.

   Status = Play%("t180 l4")	'tempo = 180, note length = 4

   Status = Play%("o3 ms")  	'octave = 3, music = staccato

   Status = Play%("cceg")   	'the first four notes

   Status = Play%(">c2")          	'up an octave, play C as a half note

   Status = Play%("<a2") 	'octave back down, A is a half note

   Status = Play%("afgag1")	'the last note here is a whole note

Example Location:

Project File	Form/BASIC File	Procedure

PLAY.MAK	PLAY.FRM	Play_Click


Poke1

assembler subroutine contained in QPRO200.DLL

Purpose:

Poke1 writes a new byte to a specified segment and address.

Declare Syntax:

Declare Sub Poke1 Lib "QPRO200.DLL" (ByVal Segment%, ByVal Address%, ByVal Byte%)

Call Syntax:

Poke1 Segment%, Address%, Byte%

Where:

Segment% and Address% indicate where the byte to be written is located, and the value held in Byte% is

then stored in that location.

Comments:

Remember that Windows can and does move items in memory without your knowledge.  It is a good

idea to obtain the segment and address of the memory area in question each time you write to that

location.  Also make sure you own the area you are manipulating.  In general, your program owns all the

memory used for its variables and arrays, but nothing else.


Poke2

assembler subroutine contained in QPRO200.DLL

Purpose:

Poke2 writes a new word (two bytes) to a specified segment and address.

Declare Syntax:

Declare Sub Poke2 Lib "QPRO200.DLL" (ByVal Segment%, ByVal Address%, ByVal Word%)

Call Syntax:

Poke2 Segment%, Address%, Word%

Where:

Segment% and Address% indicate where the word to be written is located, and the value held in Word%

is then stored in that location.

Comments:

Remember that Windows can and does move items in memory without your knowledge.  It is a good

idea to obtain the segment and address of the memory area in question each time you write to that

location.  In general, your program owns all the memory used for its variables and arrays, but nothing

else.


Power and Power2

assembler functions contained in QPRO200.DLL

Purpose:

Power and Power2 will raise any number to a power, or 2 to a power respectively, simulating Visual

Basic's x ^ n and 2 ^ n.

Declare Syntax:

Declare Function Power& Lib "QPRO200.DLL" (ByVal Y%, ByVal N%)

Declare Function Power2& Lib "QPRO200.DLL" (ByVal N%)

Call Syntax:

X%&= Power&(Y%, N%)

X& = Power2&(N%)

Where:

X& is assigned either 2 ^ N power, or Y ^ N power, as shown above.

Comments:

When used with integer values these routines are much faster than using Visual Basic's exponentiation

operator which requires floating point math.

These functions may be declared as either integer or long integer functions, depending on the expected

range of return values.


QPSegAdr

assembler subroutine contained in QPRO200.DLL

Purpose:

QPSegAdr returns a far (segmented, or long pointer) address of a variable or type structure.

QPSegAdr is equivalent to the DOS BASIC SEG function.

Declare Syntax:

Declare Function QPSegAdr& Lib "QPRO200.DLL" (Data As Any)

Call Syntax:

FarAddr& = QPSegAdr&(Data%)

Where:

FarAddr& receives the far address of the variable Data%.

Comments:

Several Windows API routines require that an element of a type structure contain the address of an array

or variable.  While arrays are passed this way to Visual Basic external procedures written in other

languages, there is no way to get this information directly in a Visual Basic program.

See Also:

LPStr, SAdd, SSeg, VarPtr, VarSeg


QPSolver

Visual Basic demonstration program contained in QPSOLVER.FRM

QPSolver is a complete environment for entering and editing variables and expressions to be evaluated

using the QuickPak Professional for Windows Evaluate function.

QPSolver is similar to a BASIC interpreter, in that you can enter assignments as immediate commands,

and then display the results using PRINT statements. Variables may be assigned from numbers or from

other variables.  All of the functions that Evaluate supports may be used; for example, you may enter an

expression such as X = ATN(Y).

Besides merely assigning and displaying variable values, you may also set up Watch expressions in

much the same way that QuickBASIC allows.  For example, the command WATCH X * (Y ^ Z) sets that

as a Watch expression, and the display will be updated with each change to X, Y, or Z.

Finally, you can save and load sessions for later editing.  The command SAVE FILENAME will create a

file named FILENAME, and store the entire context of the current session.  Likewise, entering  LOAD

FILENAME will load that file and restore the original variable values and Watch expressions.

Understand that QPSolver is not a true solver in the sense that MathCad and TKSolver are.  However,

this is the core program for a real solver we hope to eventually develop for inclusion in QuickPak

Scientific.

Example Location:

Project File	Form/BASIC File

QPSOLVER.MAK	QPSOLVER.FRM


QPUSI

assembler function contained in QPRO200.DLL

Purpose:

QPUSI (QuickPak Unsigned Integer) returns the low-word portion of a long integer.

Declare Syntax:

Declare Function QPUSI% Lib "QPRO200.DLL" (ByVal LongInt&)

Call Syntax:

LowWord% = QPUSI%(LongInt&)

Where:

LowWord receives the two lowest bytes in LongInt&.

Comments:

QPUSI is useful for those situations where you are using a long integer to store integer information

whose value may exceed 32767.  If you then need to pass that as a regular integer by value using ByVal,

the only other way to obtain just the lower word is with Peek and Poke.  QPUSI solves this problem by

directly returning just the low-word portion of the long integer.


QPWeekDay

assembler function contained in QPRO200.DLL

Purpose:

WeekDay will return the day of the week (1 through 7) given a legal DOS date in a string form.

Declare Syntax:

Declare Function QPWeekDay% Lib "QPRO200.DLL" (D$)

Call Syntax:

Day% = QPWeekDay%(D$)

Where:

D$ is a string date in the form "MMDDYY", or "MM-DD-YY", or "MM/DD/YYYY", and so forth, and Day

receives the equivalent number.

Comments:

QPWeekDay calls on an existing DOS service to do the actual calculation, therefore, the legal range of

dates is limited to that of DOS: 01-01-1980 through 12-31-2099.

Notice that QPWeekDay modifies the system date and time temporarily, and should not be called

repeatedly inside a loop.  Doing that will cause your system clock to lose time, or possibly even run

backwards!

See Also:

Num2Day and Date2Day


ReadZipX

Basic subroutine contained in ZIPSUPPT.BAS

Purpose:

ReadZipX reads header information from a PKWare .ZIP file and returns an array containing the names

of all files contained within along with size, date, time, checksum, and compression method information

for each file.

Declare Syntax:

Type ZIP	'ZIP array type (48 bytes)

   FileName As String * 12	'file name

   FileSize As Long	'uncompressed size

   FileDate As String * 10	'last mod date mm-dd-yyyy

   FileTime As String * 8 	'last mod time hh:mm:ss

   DateTime As Double	'Combined date & time VB format

   FileCRC As Long	'32-bit file CRC

   ZIPMethod As Integer	'the compression method

   Selected As Integer	'Selection flag for your use

End Type

Call Syntax:

Dim ZipInfo(1 To nZips) As ZIP

ReadZipX FileName$, ZipInfo(), ErrCd%

Where:

nZips is the number of compressed files in the .ZIP file (see the ZipCount function).  FileName$ is the

name of the .ZIP file to read.  ZipInfo() is the array to load with information, and ErrCd indicates errors

reading the specified file as follows:

  Ercd = 0 - successful completion

     1 - not a .ZIP file (first file header invalid)

     2 - open error

     3 - read error

     4 - corrupted .ZIP (invalid header other than first)

Comments:

You can use ReadZipX to obtain a list of files contained within a ZIP file.  This information allows you to

selectively expand only the files you want, and also determine the date, time and expanded size of each

file.

Also See:

The ZipCount function

Example Location:

Project File	Form/BASIC File	Procedure

CSUNZIP.MAK	CSUNZIP.FRM	ReadZip


SAdd

assembler function contained in QPRO200.DLL

Purpose:

SAdd returns the near address (offset) of the data associated with a variable-length string.

Declare Syntax:

Declare Function SAdd% Lib "QPRO200.DLL" (Variable$)

Call Syntax:

Address% = SAdd%(Variable$)

Where:

Address% receives the offset of Variable$'s data within the segment in which it resides.

Comments:

SAdd is the equivalent of the DOS BASIC function with the same name.

See Also:

LPStr, QPSegAddr, SSeg, VarPtr, and VarSeg.


ShiftIL and ShiftIR

assembler functions contained in QPRO200.DLL

Purpose:

ShiftIL shifts the bits in an integer variable a specified number of positions to the left, and ShiftIR shifts

bits to the right.

Declare Syntax:

Declare Function ShiftIL% Lib "QPRO200.DLL" (ByVal Number%, ByVal Numbits%)

Declare Function ShiftIR% Lib "QPRO200.DLL" (ByVal Number%, ByVal Numbits%)

Call Syntax:

Shifted% = ShiftIL%(Number%, Numbits%)

Shifted% = ShiftIR%(Number%, Numbits%)

Where:

Number% is the variable whose bits are to be shifted, NumBits% tells how many positions to shift them,

and Shifted% receives the resulting value.

Comments:

Shifting bits in a variable is not something that most programmers would need to do every day, but it

does have a few uses. For example, like the AddUSI function, shifting bits to the left can be used to

multiply a variable by 2, 4, 8, and so forth, without generating an overflow error that might otherwise

occur. Each time the bits in an integer are shifted to the left one position, the variable is multiplied by

two. Likewise, shifting right divides by 2 for each bit position shifted.

See Also:

ShiftLL and ShiftLR.


ShiftLL and ShiftLR

assembler functions contained in QPRO200.DLL

Purpose:

ShiftLL shifts the bits in a long integer variable a specified number of positions to the left, and ShiftLR

shifts bits to the right.

Declare Syntax:

Declare Function ShiftLL& Lib "QPRO200.DLL" (ByVal NumberLong&, ByVal Numbits%)

Declare Function ShiftLR& Lib "QPRO200.DLL" (ByVal NumberLong&, ByVal Numbits%)

Call Syntax:

Shifted& = ShiftLL&(NumberLong&, Numbits%)

Shifted& = ShiftLR&(NumberLong&, Numbits%)

Where:

NumberLong& is the variable whose bits are to be shifted, NumBits% tells how many positions to shift

them, and Shifted receives the resulting value.

Comments:

Shifting bits in a variable is not something that most programmers would need to do every day, but it

does have a few uses. See the discussion of the ShiftIL and ShiftIR routines for some possible

applications.


Sound

assembler function contained in QPRO200.DLL

Purpose:

Sound provides the same functionallity as the DOS BASIC statement of the same name.  That is, it

allows your program to generate sounds of a specific frequency and duration.

Declare Syntax:

Declare Function Sound% Lib "QPRO200.DLL" (ByVal Frequency%, ByVal Duration%)

Call Syntax:

Status% = Sound%(Frequency%, Duration%)

Where:

Frequency% is the desired frequency in Hz. (Hertz, or cycles per second), and Duration% is the length of

the sound in 1/18ths of a second.

Status% receives the return status of the function as follows:

Value: 	Explanation:

  -1	Sound was successful.  There should also be sound emanating from the PC.

   0	A previous Play or Sound statement is executing in the backround.

  +1	The Windows sound driver SOUND>DRV is not available.

Comments:

This function requires that the Windows sound driver SOUND.DRV supplied with QuickPak Professional

for Windows be installed in your Windows system directory.  The driver supplied with this package is an

updated version of the one supplied with Windows 3.0, and you may distribute it with your applications.

Since the Windows sound driver cannot accept a new sound when it is busy executing a previous one,

you must create code to check the driver's status as follows:

   Do Until Sound%(Frequency%, Duration%)

      Dummy% = DoEvents%()

   Loop

This example continually tries to execute the sound until the Sound function returns a non-zero result.  A

return status other than zero indicates that the music was accepted by the sound driver, or an error

occured preventing any sound from being created.  The DoEvents%() statement ensures that other

Windows tasks are able to execute while you are waiting for the sound driver to complete its task.

Also note that you must close the sound driver before your program ends.  You can do this using the

Play function as follows:

   Dummy% = Play%("""")

See Also:

Play


Soundex

assembler function contained in QPRO200.DLL

Purpose:

Soundex returns a value that corresponds to the sound of a word.

Declare Syntax:

Declare Function Soundex$ Lib "QPRO200.DLL" (Word$)

Call Syntax:

Code$ = Soundex$(Word$)

Where:

Word$ is any single word or proper name, and Code$ receives a four-digit Soundex code such as

"0122".

Comments:

This routine employs the standard Soundex algorithm to return a four-character string that loosely

approximates how the string sounds.

Soundex is often used for searching a database of names, when the exact spelling is not known.  To see

if one string sounds like another, you would simply compare the results that Soundex returns:

    If Soundex$(First$) = Soundex$(Second$) Then

       Print "They sound alike"

    End If

When a string is being converted to its equivalent Soundex code, vowels are removed, and consonants

that are similar are given the same value.  Note that when the same sound occurs twice or more in

succession, only one of them is counted.  Also note that with long strings the conversion ceases when all

four digits are filled.  Likewise, short strings are padded with trailing "0" characters.

The conversion codes are shown in the table below.

      Letters  B F P V C G J K Q S X Z D T L M N R

      CodeNum  1 1 1 1 2 2 2 2 2 2 2 2 3 3 4 5 5 6

Thus, the string "QuickPak" would be coded as "2212" ("QCPK").  The first consonant, "Q", is translated

to "2", and the vowels "ui" are ignored.  The "c" is then converted to "2", but the following "k" is ignored

because it has the same code as the "c".  The "P" is translated to "1", the vowel "a" is ignored, and the

final "K" is converted to "2".

It is important to understand that because of the many quirks in the English language, Soundex is not

always reliable.


SSeg

assembler function contained in QPRO200.DLL

Purpose:

SSeg returns the segment portion of a variable-length string's data.

Declare Syntax:

Declare Function SSeg% Lib "QPRO200.DLL" (Variable$)

Call Syntax:

Segment% = SSeg%(Variable$)

Where:

Segment% receives the segment which contains the data associated with Variable$.

Comments:

SSeg is the equivalent of the DOS BASIC function having the same name.

See Also:

LPStr, QPSegAddr, SAdd, VarPtr, and VarSeg.


Swap

assembler subroutine contained in QPRO200.DLL

Purpose:

Swap will quickly exchange the contents of two variables of the same type.

Declare Syntax:

Declare Sub Swap Lib "QPRO200.DLL" (Data1 As Any, Data2 As Any, ByVal TypeCode%)

Call Syntax:

Swap Data1, Data2, TypeCode%

Where:

The contents of Data1 and Data2 are exchanged, using the value in TypeCode% to indicate their data

type according to the table below.

   0 = variable length strings

  -1 = integer

  -2 = long integer

  -3 = single precision

  -4 = double precision

  -5 = currency

  -8 = variants

  +n = fixed length Type variable of n bytes.

Comments:

This function is a replacement for the DOS BASIC statement with the same name, which is not

supported by Visual Basic.

Swap allows you to quickly exchange the contents of any two variables without using an intermediate

temporary variable.  Many array processing algorithms such as deleting, inserting, and sorting are based

on exchanging elements, and the faster this can be accomplished, the better.


SysTime

assembler subroutine contained in QPRO200.DLL

Purpose:

SysTime obtains the current system time through DOS, and returns it in a string formatted to the

hundredth of a second.

Declare Syntax:

Declare Sub SysTime Lib "QPRO200.DLL" (T$)

Call Syntax:

SysTime T$

Where:

T$ must first be assigned to a length of at least eleven characters, and SysTime then fills it with the

system time in the form "HH:MM:SS:HH".

Comments:

Even though DOS (and SysTime) report the system time to a resolution of 1/100th second, it is really

only accurate to about 1/18th second or so. The PC's hardware updates the current time only that often,

so the last digit is mostly meaningless.

SysTime could be used to advantage if you want to simulate the type of display used in televised

sporting events, or to create extra tension in an action game.


Time2Num

assembler function contained in QPRO200.DLL

Purpose:

Time2Num converts a time in string form to an equivalent number of seconds after midnight.

Declare Syntax:

Declare Function Time2Num& Lib "QPRO200.DLL" (Byval T$)

Call Syntax:

Time& = Time2Num&(T$)

Where:

T$ is a time in the form of "HH:MM:SS", and Time receives an equivalent long integer value representing

the number of seconds.

Comments:

Time2Num is a powerful routine with two important uses. Besides allowing what would otherwise be an

eight character string to be packed to only four bytes, it also provides a simple and effective way to

perform time arithmetic.

Once a time has been converted to its equivalent value, you may add or subtract any number of

seconds, and then use the companion function Num2Time to obtain the result.

   T$ = "14:03:22"

   Start& = Time2Num&(T$)

   Later& = Start& + 3600

   NextHour$ = Num2Time$(Later&)

   Print "One hour after "; T$; " is "; NextHour$

Because Time2Num and Num2Time are functions they may also be used within a print or assignment

statement directly:

   Print "One hour after"; T$; " is " Num2Time$(Later&)

See Also:

Num2Time

Example Location:

Project File	Form/BASIC File	Procedure

DATETIME.MAK	DATETIME.FRM	AgePB_Click


Times2

assembler function contained in QPRO200.DLL

Purpose:

Times2 will multiply an integer variable times 2, without causing an overflow if the result exceeds 32,767.

Declare Syntax:

Declare Function Times2% Lib "QPRO200.DLL" (ByVal Number%)

Call Syntax:

Value% = Times2%(Number%)

Where:

Value receives the value of Number% multiplied by 2.

Comments:

Times2 is a fairly specialized routine, but it is included because we have needed it several times in our

own programming. For example, it can be used to determine the number of bytes in an integer array,

given the number of elements. Notice that when the returned value exceeds 32,767, Visual Basic will

consider it to be a negative number.


TrapInt

assembler function contained in QPRO200.DLL

Purpose:

TrapInt will constrain an incoming value to within a specified upper and lower limit.

Declare Syntax:

Declare Function TrapInt% Lib "QPRO200.DLL" (ByVal Value%, ByVal LoLimit%, _

  ByVal HiLimit%)

Call Syntax:

Limited% = Trapint%(Value%, LoLimit%, HiLimit%)

Where:

Value% is the value to be constrained, LoLimit% is the minimum acceptable value, and HiLimit% is the

maximum acceptable value.

Comments:

TrapInt is very useful for avoiding an "Illegal function call" error, for example when attempting to locate

to an illegal row or column. The examples on the following page show how TrapInt might be used for this

purpose, along with the Visual Basic code that would otherwise be needed.

   Sub Form_MouseMove (Button As Integer, Shift As Integer, X As Single, _

      Y As Single)

      Ctrl.Move TrapInt%(X!, (Left), Left + Width - Ctrl.Width)

vs.

   If X! << Form.Left Then

     X! = Form.Left

   ElseIf X! >> Form.Left + Form.Width - Ctrl.Width Then

     X! = Form.Left + Form.Width - Ctrl.Width

   End If

Ctrl.Move X!


VarPtr

assembler function contained in QPRO200.DLL

Purpose:

VarPtr returns the near address (offset) of a non-string variable.

Declare Syntax:

Declare Function VarPtr% Lib "QPRO200.DLL" (Variable As Any)

Call Syntax:

Address% = VarPtr%(Variable%)

Where:

Address% receives the offset of Variable% within the segment in which it resides.

Comments:

VarPtr is equivalent to the DOS BASIC function having the same name.

See Also:

LPStr, QPSegAdr, SAdd, SSeg and VarSeg.


VarSeg

assembler function contained in QPRO200.DLL

Purpose:

VarSeg returns the segment portion of a non-string variable's far address.

Declare Syntax:

Declare Function VarSeg% Lib "QPRO200.DLL" (Variable As Any)

Call Syntax:

Segment% = VarSeg%(Variable%)

Where:

Segment% receives the segment which contains Variable%.

Comments:

VarSeg is equivalent to the DOS BASIC function having the same name.

See Also:

LPStr, QPSegAdr, SAdd, SSeg and VarPtr.


String Manipulation Routines

Ascii

Blanks

Compact

CSNull

Encrypt and Encrypt2

Far2Str

FUsing

InCount and InCount2

InCountTbl

InstrTbl and InstrTbl2

InstrTblB and InstrTblB2

LongestStr

LowAscii

Lower

LowerTbl

NotInstr

ParseString

ProperName

QInstr and QInstr2

QInstrB and QInstrB2

QPLen

QPTrim

RemCtrl

ReplaceChar and ReplaceChar2

Sequence

SpellNumber

TrimZStr

Upper

UpperTbl


Ascii

assembler function contained in QPRO200.DLL

Purpose:

Ascii obtains the ASCII value for a string exactly as Visual Basic's Asc function does, but it will not cause an "Illegal Function Call" error if the string is null.

Declare Syntax:

Declare Function Ascii% Lib "QPRO200.DLL" (Character$)

Call Syntax:

A% = Ascii%(Character$)

Where:

Character$ is any string, and A% receives the ASCII value of its first character. If the string is null, Ascii instead returns -1.

Comments:

Ascii was created to avoid the extra steps needed to insure that a string isn't null before Asc can be used. Attempting to obtain the ASCII value of a null string using the Visual Basic Asc() function will cause an Illegal Function Call error.

Before Ascii, you would need to add additional code to your programs as shown below:

If Len(x$) Then

  If Asc(x$) => 65 Then

    Print "You pressed a letter key"

  End If

End If

ASCII instead lets you use:

If Ascii%(x$) =>> 65 Then

   Print "You pressed a letter key"

End If

Example Location:

Project File	Form/BASIC File	Procedure

COMPINT.MAK	FNSPREAD.FRM	QPROUND


Blanks

assembler function contained in QPRO200.DLL

Purpose:

Blanks will report the number of leading blanks in the specified string. Both Chr$(32) and the Chr$(0) "null" character are recognized.

Declare Syntax:

Declare Function Blanks% Lib "QPRO200.DLL" (Work$)

Call Syntax:

NumBlanks% = Blanks%(Work$)

Where:

NumBlanks% receives the number of leading blank and/or Chr$(0) bytes in Work$.

Comments:

Blanks is useful for finding where within a string the actual text begins. Other methods would require either stepping through the string a character at a time using Mid$, or comparing the length of the string with the length of a copy that had been processed with Ltrim$.

Either of those methods would be extremely slow compared to using Blanks, and of course LTrim$ does not consider Chr$(0) as a blank. Recognizing Chr$(0) is important with fixed-length strings, because Visual Basic uses that to initialize each string.


Compact

assembler function contained in QPRO200.DLL

Purpose:

Compact compresses a string by removing all embedded blanks.

Declare Syntax:

Declare Function Compact$ Lib "QPRO200.DLL" (Old$)

Call Syntax:

NewString$ = Compact$(Old$)

Where:

NewString$ is assigned from Old$, but with all Chr$(32) blanks removed.

Comments:

Unlike most functions, the original string is also compacted. In designing Compact we would have preferred to not tamper with the original string. However, that would require reserving sufficient memory to hold the longest possible string that Compact would act on.

If the original string must be preserved you should use parentheses to force Visual Basic to create a copy of it.

NewString$ = Compact$((Old$))

Example Location:

Project File	Form/BASIC File	Procedure

EVALUATE.MAK	EVALUATE.FRM	Evaluate


CSNull

assembler function contained in QPRO200.DLL

Purpose:

CSNull will report if the specified string is either null, or is filled only with blank or Chr$(0) characters.

Declare Syntax:

Declare Function CSNull% Lib "QPRO200.DLL" (Work$)

Call Syntax:

Empty% = CSNull%(Work$)

Where:

Empty is assigned -1 (true) if the string is effectively null, or 0 (false) if it is not.

Comments:

CSNull was developed to overcome the clumsy code that is otherwise needed to determine if a string contains useful characters. The usual way to do this would be either:

   If Work$ = "" Or Work$ = Space$(Len(Work$)) Then

   . . .                                          'It's null

or

   If LTrim$(RTrim$(Work$)) = "" Then

   . . .                                          'Also null

Besides being considerably faster, CSNull considers a Chr$(0) byte the same as a Chr$(32) space. It is important to recognize Chr$(0) because that is what Visual Basic uses to initialize fixed-length strings.


Encrypt and Encrypt2

assembler subroutines contained in QPRO200.DLL

Purpose:

Encrypt and Encrypt2 will take any conventional or fixed-length string, and encrypt it using a password you provide.

Declare Syntax:

Declare Sub Encrypt Lib "QPRO200.DLL" (X$, Password$)

Declare Sub Encrypt2 Lib "QPRO200.DLL" (X$, Password$)

Call Syntax:

Encrypt X$, Password$

Encrypt2 X$, Password$

Where:

X$ is the string to be encrypted, and Password$ is the password to be used. Notice that these routines are intended both to encrypt the string, and to decrypt it again later using the same password. Also notice that when using Encrypt2, the password must be less than 44 characters long.

Comments:

Encrypt2 is based on a fairly simple algorithm that is also reasonably secure. While the Secret Service or the FBI will probably find it inadequate, it would be pretty difficult for most folks to crack. Encrypt is somewhat less secure, and is provided solely for compatibility with earlier versions of QuickPak and QuickPak Professional. Also for compatibility, the FileCrypt Visual Basic subprogram still uses the original Encrypt routine. We recommend that you modify the Visual Basic source code to use Encrypt2, unless you already have files that were encrypted using Encrypt.

There are a few points to consider that will maximize the security provided by Encrypt.

   1) Use a long password.

   2) Don't select a password that could be easily guessed.

   3) Double-encrypt the string.

Using a long password will minimize the chance of creating a recognizable repeating pattern.

If you use a password such as your last name or birthday, you will make it that much easier for someone to guess. Of course, don't go overboard with something like "J@GE#SVEY34KJFDBE!OSNBDN", or you'll never remember it yourself. MCI Mail uses a clever system of nonsense letters that are also easy to pronounce, such as "YAXALUPA" and "ZIGUMINI".

If the password is first encrypted with another one, the protection will be much more effective. Of course, you must then decrypt the string twice in the reverse order later.

Encrypt and Encrypt2 use the Xor command to apply the characters in the password against those in the string being encrypted. The equivalent Visual Basic algorithm for Encrypt is:

L = Len(Password$)

For X = 1 To Len(X$)

   Pass = Asc(Mid$(PassWord$, (X Mod L) - L * _

               ((x Mod L) = 0), 1))

   Mid$(x$, X, 1) = Chr$(Asc(Mid$(x$, X, 1)) Xor Pass)

Next

Encrypt2 is nearly identical, except the characters in the password are altered as the encryption progresses.

Example Location:

Project File	Form/BASIC File	Procedure

FCRYPT.MAK	FCRYPT.FRM	SCryptPB_Click


Far2Str

assembler function contained in QPRO200.DLL

Purpose:

Far2Str will retrieve an ASCIIZ string from anywhere in memory, and return it as a conventional Visual Basic string.

Declare Syntax:

Declare Function Far2Str$ Lib "QPRO200.DLL" (ByVal Segment%, ByVal_

Address%)

Call Syntax:

Work$ = Far2Str$(Segment%, Address%)

Where:

Segment% and Address% specify where in memory the string is located.

Comments:

An ASCIIZ string is a string whose end is marked with a Chr$(0) zero byte (hence the "Z"), such as the strings returned by DOS and Windows API calls.

If you are using an array to store far string data, then you can save a few bytes each time Far2Str is used by declaring and calling it like this:

   Declare Function Far2Str$ Lib"QPRO200.DLL"(FarString As Any)

   Work$ = Far2Str$(Array(0))


FUsing

assembler function contained in QPRO200.DLL

Purpose:

FUsing accepts an incoming number and image string, and returns it as a formatted string much like DOS BASIC's PRINT USING would.

Declare Syntax:

Declare Function FUsing$ Lib "QPRO200.DLL" (Number$, Image$)

Call Syntax:

Formatted$ = FUsing$(Number$, Image$)

Where:

Number is any number whether integer, single, or double precision, and Image$ indicates how the result is to be formatted. If the number will not fit within the allotted space, the first digit of the returned string will be replaced with a percent sign (%).

Comments:

Because FUsing has been designed as a function, it must be declared before it may be used.

By using the Str$ function, we are letting Visual Basic do most of the dirty work to interpret the floating point numbers. This also lets FUsing accept any type of numeric variable. Normally, assembler routines must be written to expect one type of variable only.  If you do not use Visual Basic's Str$ function and directly pass a string to FUsing, be sure to add an extra space to the beginning of the number string that you are formatting, i.e., "1234" becomes "  1234".  Most of the formatting codes that PRINT USING recognizes are supported, including commas, dollar signs, and leading asterisks.

It is very important that you not include any blank spaces within the image string.  For example "##.##" is perfectly legal, while "##.##" will not work as expected.

The table below summarizes FUsing's capabilities:

     #	represents each digit position

     . 	specifies a decimal point

     +	causes the sign of the number (+ or -) to be added (the sign must be the first character in the field)

    **	replaces leading spaces in the field with asterisks

    $$	adds a dollar sign to the left of the number

   **$	combines the effects of ** and $$

    '	specifies that commas are to be added to the formatted string

Although DOS BASIC accepts multiple commas in the image string, FUsing requires only one.  If there is a decimal point within the string, the comma must be placed just before it.  Otherwise, the comma must be the last character.

Example Location:

Project File	Form/BASIC File	Procedure

COMPINT.MAK	FNSPREAD.FRM	QPROUND


InCount and InCount2

assembler functions contained in QPRO200.DLL

Purpose:

InCount will quickly report how many times one string occurs within another, and the search string may contain any number of "?" wild cards. InCount2 works the same way, but searching is case-insensitive.

Declare Syntax:

Declare Function InCount% Lib "QPRO200.DLL" (Source$, Search$)

Declare Function InCount2% Lib "QPRO200.DLL" (Source$, Search$)

Call Syntax:

Count% = InCount%(Source$, Search$)

Count% = InCount2%(Source$, Search$)

Where:

Source$ and Search$ are either conventional or fixed-length strings, and Count receives the number of times Search$ occurs in Source$.

Comments:

InCount is used to advantage by the QuickPak Professional for Windows ExpandTab$ and Parse$ functions. Of course, it could have many uses in your own programs as well.

For example, if you accept command line switches such as "/D" or "/T", InCount would quickly tell how many have been entered by the user:

NumSwitches = InCount(Command$, "/")

InCount2 performs the identical function, except that it ignores capitalization in both strings as it searches. Both routines accept any number of the "?" wild cards in the search specification.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOINST.MAK	INSTVAR.FRM	ICPB_Click


InCountTbl

assembler function contained in QPRO200.DLL

Purpose:

InCountTbl returns the number of characters in a string that match any of the characters in a table.

Declare Syntax:

Declare Function InCountTbl% Lib "QPRO200.DLL" (Source$, Table$)

Call Syntax:

Count% = InCountTbl%(Source$, Table$)

Where:

Source$ is the string being examined, and Table$ holds a list of characters to search for.

Comments:

InCountTbl is useful in a variety of situations, for example when searching for one or more printer control codes that have been imbedded within a string. In the case of a print routine that must also deal with margins, it would be necessary to know the length of each string, but without regard to the printer codes.

The following example shows this in context.

Work$ = Chr$(15) + "This is a test"             '15 is an Epson code

Table$ = Chr$(27) + Chr$(15) + Chr$(2)          'some control codes

ActualLen = Len(Work$) - InCountTbl%(Work$, Table$)

                                                'the answer is 14


InstrTbl and InstrTbl2

assembler functions contained in QPRO200.DLL

Purpose:

InstrTbl and InstrTbl2 will quickly search a string for the first occurrence of any characters that are specified in a table string. InstrTbl honors capitalization, while InstrTbl2 does not.

Declare Syntax:

Declare Function InstrTbl% Lib "QPRO200.DLL" (ByVal Start%, Source$,_

 Table$)

Declare Function InstrTbl2% Lib "QPRO200.DLL" (ByVal Start%, _

Source$, Table$)

Call Syntax:

Position% = InstrTbl%(Start%, Source$, Table$)

Position% = InstrTbl2%(Start%, Source$, Table$)

Where:

Start% specifies where in the source string searching is to begin, Source$ is the string being examined, and Table$ contains one or more characters to search for. Position then receives the position where the first match was found. If there is no match, Position will receive zero.

Comments:

Unlike Visual Basic's InStr function, the Start% parameter must always be given. If you intend to search the entire source string, then Start% should be set to 1. A typical use of InstrTbl2 is given below:

Source$ = "FIND ME HIDING HERE"              'search this string

Table$ = "aeiou"                             'find the first vowel

Position = InstrTbl2%(1, Source$, Table$)

                                             'Position receives 2

See Also:

The related routines InstrTblB and InstrTblB2, search backwards through a string.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOINST.MAK	INSTRVAR.FRM	IT2PB_Click


InstrTblB and InstrTblB2

assembler functions contained in QPRO200.DLL

Purpose:

InstrTblB and InstrTblB2 will search a string backwards for the first occurrence of any characters that are specified in a table. InstrTblB honors capitalization, while InstrTblB2 does not.

Declare Syntax:

Declare Function InstrTblB% Lib "QPRO200.DLL" (ByVal Start%, _

Source$, Table$)

Declare Function InstrTblB2% Lib "QPRO200.DLL" (ByVal Start%,_

Source$, Table$)

Call Syntax:

Position% = InstrTblB%(Start%, Source$, Table$)

Position% = InstrTblB2%(Start%, Source$, Table$)

Where:

Start% specifies where in the source string searching is to begin, Source$ is the string being examined, and Table$ contains one or more characters to search for. Position then receives the position where the first match closest to the end of the string was found.

Comments:

Unlike Visual Basic's InStr function, the Start% parameter must always be given. If you intend to search the entire source string, then Start% should be set to -1. A typical use of InstrTblB is given below:

Source$ = "FIND 987 HIDING"                     'search this string

Table$ = "0123456789"                           'find the last digit

Position = InstrTblB%(-1, Source$, Table$)

                                                'Position receives 8

See Also:

The related routines InstrTbl and InstrTbl2, search forward through the string.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOINST.MAK	INSTRVAR.FRM	ITBPB_Click

ITB2PB_Click


LongestStr

assembler function contained in QPRO200.DLL

Purpose:

LongestStr returns the length of the longest string in an entire array.

Declare Syntax:

Declare Function LongestStr% Lib "QPRO200.DLL" (Array$, ByVal_

NumEls%)

Call Syntax:

MaxLength% = LongestStr%(Array$(1), NumEls%)

Where:

NumEls specifies the number of elements to test and MaxLength receives the length of the longest string contained in Array$().

Comments:

LongestStr is useful in a variety of situations, for example to determine how wide to make a menu that will display an entire array.


LowAscii

assembler subroutine contained in QPRO200.DLL

Purpose:

LowAscii will strip the "high bit" from all of the characters in a specified string.

Declare Syntax:

Declare Sub LowAscii Lib "QPRO200.DLL" (X$)

Call Syntax:

LowAscii X$

Where:

X$ is the string to be processed.

Comments:

LowAscii is primarily intended to fix data that is in the format used by WordStar and some other word processors. It is equally useful for printers that can't accept characters with an ASCII value greater than 127.

See Also:

Translate and RemCtrl


Lower

assembler subroutine contained in QPRO200.DLL

Purpose:

Lower will quickly convert all of the alphabetic characters in a specified string to lower case.

Declare Syntax:

Declare Sub Lower Lib "QPRO200.DLL" (X$)

Call Syntax:

Lower X$

Where:

X$ is the string to be processed.

Comments:

Even though Visual Basic provides a built-in LCase$() function, Lower is provided because it is considerably faster. One of the problems with LCase$ (and UCase$) is that they must be used as a function. That is, in order to convert a string it must be reassigned which takes time:

   X$ = LCase$(X$)

Lower instead scans through the named string very quickly, converting only the upper case alphabetic characters.

See Also:

The related routine Upper.


LowerTbl

assembler subroutine contained in QPRO200.DLL

Purpose:

LowerTbl will convert all of the characters in a string to lower case, and it also looks in a supplied table to determine how to handle foreign characters.

Declare Syntax:

Declare Sub LowerTbl Lib "QPRO200.DLL" (Work$, Table$)

Call Syntax:

LowerTbl Work$, Table$

Where:

Work$ is the string to be processed, and Table$ holds one or more pairs of lower and upper case extended characters.

Comments:

LowerTbl was written to accommodate our European friends, although it could also be used as a general purpose table-driven character substitution routine. The table string is comprised of pairs of characters, with the first in each pair being the lower case version, and the second its upper case counterpart.

Though we could have hard-coded the conversions into the routine, it would need to be changed for different languages. For example, the character conversions that are used for German are not necessarily the same as those for other languages. A typical table would be assigned like this:

   Table$ = "„Ž™�š¤¥"

Notice that normal characters are converted to lower case regardless of the table, and Table$ is needed solely to accommodate the additional characters.

See Also:

The related routine UpperTbl, which converts a string to upper case.


NotInstr

assembler function contained in QPRO200.DLL

Purpose:

NotInstr reports the first location in one string that does not match any of the characters in another string.

Declare Syntax:

Declare Function NotInstr% Lib "QPRO200.DLL" (ByVal Start%, _

Searched$, Table$)

Call Syntax:

Position% = NotInstr%(Start%, Searched$, Table$)

Where:

Position receives the offset of the first character in Searched$ that doesn't match any of the characters in Table$

Comments:

NotInstr is not strictly the inverse of Visual Basic's InStr function.  Where InStr compares entire strings, NotInstr uses a table of single characters.  The first position in the searched string that doesn't match any of them is then returned by the function.  You can tell NotInstr to begin at any character in the searched string, or use a Start value of 1 to consider the entire string.

The following example displays 2, because the second character in Source$ does not match any of the

characters in Table$.

Source$ = "QUICKPAK"

Table$ = "QKP"

Print NotInstr%(1, Source$, Table$) 'prints 2


ParseString

Assembler function contained in QPRO200.DLL

Purpose:

ParseString accepts a string containing delimited information, and returns portions of that string each time it is invoked.

Declare Syntax:

Declare Function ParseString$ Lib "QPRO200.DLL" (CurPos%, Work$,_

 Delimit$)

Call Syntax:

Item$ = ParseString$(CurPos%, Work$, Delimit$)

Where:

CurPos% tells where in the string to begin processing, Work$ is the string being read, and Delimit$ is a table of one or more delimiting characters. Item$ then receives the next delimited component from Work$.

Comments:

This is an enhanced version of PDQParse that we include with our P.D.Q. library.  Each time ParseString is invoked it returns the next portion of the string, much like DOS BASIC's Read returns the next available Data item in a list.  The example below shows how ParseString is set up and used.

CurPos% = 1                  'start reading at the beginning

Delimit$ = ";/-,"            'any of these characters will delimit

Work$ = Environ$("PATH")     'for example:  "C:\DOS;C:\UTIL;C:\QB4"

Do                           'read and print each PATH item

   ThisItem$ = ParseString$(CurPos%, Work$, Delimit$)

   Print ThisItem$

Loop While CurPos%           'until there are no more

The CurPos% (current position) parameter lets you indicate where to begin reading, and would usually be set to 1 initially.  If CurPos% is less than 1 (0 or negative) it is forced to 1.  If CurPos% points past the end of the string, then ParseString will return a null (zero-length) string.


ProperName

assembler subroutine contained in QPRO200.DLL

Purpose:

ProperName converts the first letter of each word in a string to upper case.

Declare Syntax:

Declare Sub ProperName Lib "QPRO200.DLL" (Work$)

Call Syntax:

ProperName Work$

Where:

Work$ is typically a name such as "john doe, jr.", and ProperName converts it to "John Doe, Jr.".

Comments:

ProperName is useful for automatically enforcing the correct capitalization on name fields in a database program. We considered writing ProperName to also convert the remaining characters to lower case, but that could have introduced undesirable side effects. For example, if the incoming name was "MacDonald", the first "D" would no longer be correct. Even though the routine could be written to search for all occurrences of "Mc" and "Mac", it would still fail on names such as "DeVito" or "O'Malley".


QInstr and QInstr2

assembler functions contained in QPRO200.DLL

Purpose:

QInstr serves the same purpose as Visual Basic's InStr function, except it accepts any number of "?" wild cards. QInstr2 is similar, but it ignores capitalization when examining the strings.

Declare Syntax:

Declare Function QInstr% Lib "QPRO200.DLL" (ByVal Start%, _

Source$, Search$)

Declare Function QInstr2% Lib "QPRO200.DLL" (ByVal Start%, _

Source$, Search$)

Call Syntax:

Position% = QInstr%(Start%, Source$, Search$)

Position% = QInstr2%(Start%, Source$, Search$)

Where:

Start% tells QInstr where in the string to start looking, and Source$ is the string being examined. Search$ is the string whose position in Source$ is being sought, and Position% receives where in Source$ the first occurrence of Search$ is located.

Comments:

Unlike Visual Basic's InStr where the starting position is an optional parameter, it must be given when using QInstr and QInstr2. An assembler routine simply cannot be called with a different number of parameters than it expects.

To search a string starting at the beginning, set Start% to 1. Using 0 will always return 0 for Position, which is what Visual Basic's InStr does.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOINST.MAK	INSTRVAR.FRM	Q1PB_Click

Q12PB_Click


QInstrB and QInstrB2

assembler functions contained in QPRO200.DLL

Purpose:

QInstrB is similar to the QuickPak Professional for Windows QInstr function, except it scans the source string backwards looking for a specified substring. QInstrB2 is an alternate version that ignores capitalization in both strings. Like QInstr and QInstr2, these functions accept any number of "?" wild cards to match any character.

Declare Syntax:

Declare Function QInstrB% Lib "QPRO200.DLL" (ByVal Start%, _

Source$, Search$)

Declare Function QInstrB2% Lib "QPRO200.DLL" (ByVal Start%, _

Source$, Search$)

Call Syntax:

Position% = QInstrB%(Start%, Source$, Search$)

Position% = QInstrB2%(Start%, Source$, Search$)

Where:

Start% tells QInstrB where in the string to start looking, and Source$ is the string being examined. Search$ is the string whose position in Source$ is being sought, and Position% receives where in Source$ the last occurrence of Search$ is located. If Start% is set to -1, then the entire string is examined starting at the last character.

Comments:

QInstrB and QInstrB2 are useful in a variety of situations. For example, to isolate the path portion of a complete file name you would need to know where in the string the last backslash (\) is located.  Unlike Visual Basic's InStr where the starting position is an optional parameter, it must be given when using QInstrB and QInstrB2. An assembler routine cannot be called with a different number of parameters than it expects. Even though searching is performed backwards, the returned position indicates where in the source string the search string begins.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOINST.MAK	INSTRVAR.FRM	Q1BPB_Click

Q12BPB_Click


QPLen

assembler function contained in QPRO200.DLL

Purpose:

QPLen serves the same purpose as Visual Basic's Len function, and is provided solely for compatibility with previous version of QuickPak Professional.

Declare Syntax:

Declare Function QPLen% Lib "QPRO200.DLL" (Work$)

Call Syntax:

Length% = QPLen%(Work$)

Where:

Length is assigned to the length of Work$.


QPTrim

assembler function contained in QPRO200.DLL

Purpose:

QPTrim serves the same purpose as the Visual Basic Trim function except that it removes leading and trailing null (Chr$(0)) characters as well as spaces.

Declare Syntax:

Declare Function QPTrim$ Lib "QPRO200.DLL" (StrVar$)

Call Syntax:

Trimmed$ = QPTrim$(StrVar$)

Where:

StrVar$ is a string containing leading and or trailing spaces or null characters.  The string can be either a variable, or a fixed length string

Comments:

This function can be used to trim fixed length strings that often contain null characters, or strings passed to dll routines that return ASCIIZ strings.

Also See:

TrimZStr


RemCtrl

assembler subroutine contained in QPRO200.DLL

Purpose:

RemCtrl quickly scans through a given string, and replaces any control characters with a specified new character.

Declare Syntax:

Declare Sub RemCtrl Lib "QPRO200.DLL" (X$, Replace$)

Call Syntax:

RemCtrl X$, Replace$

Where:

X$ is the string to be processed, and the first character in Replace$ is used as a replacement. The replacement may also be specified to be a blank space by using a null string for Replace$.

Comments:

Embedded control characters have long been a problem for programmers, because so many text files contain them, and so many printers can't handle them. A control character is any character that has an ASCII value less than 32.

Control characters are often used in communications programs to tell the modem at the other end when to start or stop transmission. Also, many word processors use various control characters to indicate margin settings and other formatting information.

RemCtrl provides a clean way to remove those characters from a string that will be printed.

Even though RemCtrl merely substitutes a new character and doesn't truly remove the control

characters, that would be easy to do as well. Simply specify an unlikely replacement such as Chr$(0) or Chr$(255), and then use the ReplaceString function to replace that with a null string.

See Also:

The related routine LowAscii.


ReplaceChar and ReplaceChar2

assembler subroutines in QPRO200.DLL

Purpose:

ReplaceChar replaces all occurrences of a specified character in a string with a different character. ReplaceChar2 is similar, but when searching it ignores capitalization.

Declare Syntax:

Declare Sub ReplaceChar Lib "QPRO200.DLL" (Source$, OldChar$,_

NewChar$)

Declare Sub ReplaceChar2 Lib "QPRO200.DLL" (Source$, OldChar$,_

NewChar$)

Call Syntax:

ReplaceChar Source$, OldChar$, NewChar$

ReplaceChar2 Source$, OldChar$, NewChar$

Where:

Source$ is the string in which the characters are being replaced, OldChar$ is the character to replace, and NewChar$ is what it is to be replaced with.

Comments:

These routines are intended to replace single characters only. They will of course replace more than one occurrence of the character automatically, but only the first letter in OldChar$ and NewChar$ is examined.


Sequence

assembler subroutine contained in QPRO200.DLL

Purpose:

Sequence will increment the characters in a string.

Declare Syntax:

Declare Sub Sequence Lib "QPRO200.DLL" (Work$)

Call Syntax:

Sequence Work$

Where:

Work$ is the string to be sequenced.

Comments:

It is common in database applications to utilize sequenced fields, perhaps to assign account or part numbers automatically. For example, if the last customer account number on record is, say, "AAA-0134-52", Sequence will increment it to be "AAA-0134-53". Likewise, if the string were "009-AABD-99", Sequence would change it to "009-AABE-00".  Sequence will increment the characters in any of the following three categories _ digits, upper case letters, and lower case letters. Imbedded delimiters are unaffected, and "wrapping" will extend across any blanks or non-numeric/ alphabetic characters.Sequence requires that the string be initially "seeded" with a starting value such as "0000000001". When all available digit positions have been exhausted, Sequence will wrap around without indicating an overflow. That is, "999" will be sequenced to "000". It is therefore up to you to ensure that a sufficient number of character positions be initially set aside.


SpellNumber

Visual Basic function contained in SPELLNUM.BAS

Purpose:

SpellNumber accepts a number in the form of a string such as "12345", and returns a spelled-out English equivalent in the form of "Twelve Thousand Three Hundred Forty Five".

Syntax:

English$ = SpellNumber$(STR$(Number))

Where:

Number may be any type of variable or value (integer, single precision, and so forth), and English$ receives the spelled-out equivalent.

Comments:

SpellNumber processes the whole number portion of the value only. Because you may want to print or display the spelled-out answer as values or as dollar amounts, we have instead shown how to deal with the fractional portion in the demonstration included in SPELLNUM.BAS.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOSFN.MAK	FNSDEMO.FRM	SpellNumPB_Click


TrimZStr

assembler function contained in QPRO200.DLL

Purpose:

TrimZStr accepts an ASCIIZ string that may be followed by garbage characters and returns a Visual Basic HlStr (conventional variable) string.  The function truncates the string at the first occurrence of an ASCIIZ or CHR$(0).

Declare Syntax:

Declare Function TrimZStr$ Lib "QPRO200.DLL" (ZString$)

Call Syntax:

NewString$ = TrimZStr$(ZString$)

Where:

ZString$ is a CHR$(0) terminated string.

NewString$ is a conventional BASIC string.

Comments:

TrimZStr$ was written to be used with CSEnumFonts to return the font name from a buffer containing the name followed by garbage.  The syntax for returning a name from the CSEnumFonts and the LogFont structure is:

FontName$ = TrimZStr$((LfArray(Y%).IfFaceName))


Upper

assembler subroutine contained in QPRO200.DLL

Purpose:

Upper will quickly convert all of the alphabetic characters in a specified string to upper case.

Declare Syntax:

Declare Sub Upper Lib "QPRO200.DLL" (X$)

Call Syntax:

Upper X$

Where:

X$ is the string to be processed.

Comments:

Even though Visual Basic provides a built-in UCase$() function, Upper is provided because it is considerably faster. One of the problems with UCase$ (and LCase$) is that they must be used as a function. That is, in order to convert a string it must be reassigned which takes time:

   X$ = UCase$(X$)

Upper instead scans through the named string very quickly, converting only the lower case alphabetic characters.

See Also:

Lower


UpperTbl

assembler subroutine contained in QPRO200.DLL

Purpose:

UpperTbl will capitalize all of the characters in a string, and it also looks in a supplied table to determine how to handle foreign characters.

Declare Syntax:

Declare Sub UpperTbl Lib "QPRO200.DLL" (Work$, Table$)

Call Syntax:

UpperTbl Work$, Table$

Where:

Work$ is the string to be capitalized, and Table$ holds one or more pairs of lower and upper case extended characters.

Comments:

UpperTbl was written to accommodate our European friends, although it could also be used as a general purpose table-driven character substitution routine. The table string is comprised of pairs of characters, with the first in each pair being the lower case version, and the second its upper case counterpart. Even though we could have hard-coded the conversions into the routine, it would need to be changed for different languages. For example, the character conversions that are used for German are not necessarily the same as those for other languages. A typical table would be assigned like this:

   Table$ = "„Ž™�š¤¥"

Notice that normal characters are converted to upper case regardless of the table, and Table$ is needed solely to accommodate the additional characters.

See Also:

The related routine LowerTbl converts a string to lower case.


Windows Specific Routines

AddIncr

Array2CB and Array2List

Bitmap2DIB

Bmp2Clip

BoxControlOnControl

BoxControlOnForm

BoxDrawControl

BoxDrawForm

CaptureBmp

CBFindString

CB2Array and List2Array

CB2File and List2File

Clip2Bmp

ControlHWnd

CopyBmp

CSCircle

CSEnumFonts

CSLine

CSPrint

CtlFrame

DeleteBmp

DepthFrameCtrl

DepthFrameFrm

DisplayBmp

DoCheckBox

DoControlLabelEmbossed

DoFormLabelEmbossed

DoLabelOnControl

DoLabelOnForm

DoOptionButton

EnumWins

File2CB and File2List

FillFormClientArea

FillPicture

FramePictureOnControl

FramePictureOnForm

GenFrame

GetSystemResources

hCtl2Ndx

hWnd2Ndx

Icon2Cursor

Initialize3DFX

LBFindString

LBGetSelCount

LBGetSelection

LBGetTopIndex

LBResetContent

LBSetTabStops

LineOnForm

LineOnPicture

LoadBmp

ModeInfo

MonoPrint

PaintFrameCtrl

PaintFrameFrm

PassPicClickToCheck

PassPicClickToOption

PrintBmp

SaveBmp

SaveCSPicture

SelectRect

SetBmpData

SplitColor

SysDir

WinDir

WinVer

X/YPixel2Twp

X/YTwp2Pixel


AddIncr

assembler function contained in QPRO200.DLL

Purpose:

AddIncr takes a long address and an offset value to add to it, and calculates a new long address.  AddIncr does the special selector math required by Windows and the protected mode environment.  If the new pointer is not valid, the function returns the original address value.

Declare Syntax:

Declare Function AddIncr& Lib "QPRO200.DLL" (ByVal Current&, _

ByVal AddValue&)

Call Syntax:

NewAddress& = AddIncr&(Current&, Addvalue&)

Where:

Current& is the current long pointer (address), AddValue& is the number of bytes to add and

NewAddress& returns a location AddValue& bytes higher than Current&

Comments:

AddIncr is useful in loading large amounts of data from disk into global memory using Windows API

calls.

Example Location:

Project File	Form/BASIC File	Procedure

DEMOBMP.MAK	BITMAP.BAS	LoadBmp


Array2CB and Array2List

assembler routines contained in QPRO200.DLL

Purpose:

Array2List and Array2CB fill a List or Combo box in one operation from a conventional (not fixed-length) string array.  The routines turn off the display while filling, and refresh it when completed to minimize screen flicker.

Declare Syntax:

Declare Sub Array2CB Lib "QPRO200.DLL" (List As Control, Array$,_

ByVal NumEls%) Declare Sub Array2List Lib "QPRO200.DLL" (List _

As Control, Array$, ByVal NumEls%)

Call Syntax:

Array2CB ComboName, Array$(Start), NumEls%

Array2List ListName, Array$(Start), NumEls%

Where:

ComboName or ListName is the name of a list box or combo box control.  Array$(Start) is the starting element of the source array.  NumEls% specifies the number of array elements to copy.

Comments:

Array2CB and Array2List use the AddString message to fill their respective controls.  If you want to replace the contents in a List or Combo box you must clear the box first.  Subsequent calls to Array2List and Array2CB will continue to add items.

If the List or Combo box runs out of memory (yes, that is possible!), then Array2CB and Array2List will fill only what they can. Note that Array2List can be used with the QPList custom list box control.

Also See:

Also see File2CB and File2List which fill a combo or list box from a sequential file.


Bitmap2DIB

Visual Basic Subroutine contained in BITMAP.BAS

Purpose:

Bitmap2DIB copies the bits of a bitmap in DIB form into a Global array, and fills the BInfo element (of type BITMAPINFO) with palette data.

Syntax:

Call Bitmap2DIB (Bmp)

Where:

Bmp is a BMPTYPE type variable that contains the bitmap information

Comments:

This routine is useful if you need to edit the actual bits of a bitmap. After calling Bitmap2DIB, the Bits() array will contain the bits of the bitmap. If you want to make permanent edits to the bitmap, you must call the Windows CreateDIBitmap to create a new hBitmap.

The BMPTYPE structure is as follows:

Type BMPTYPE

  File As BITMAPFILEHEADER 	'-- 14 Bytes

  Info As BITMAPINFOHEADER 	'-- 40 Bytes

  BInfo As BITMAPINFO 	'-- 1064 Bytes

  hBitmap As Integer 	'-- Handle to the bitmap

  hPalette As Integer 	'-- Handle to the palette

  FileName As String * 50 	'-- BMP file name (for your use)

  hWnd As Integer  	'-- Handle to display window

  Top As Integer  	'-- Top pixel position inside window

  Left As Integer  	'-- Left pixel position inside window

  ZoomFactor As Single 	'-- Zoom In = 1, Zoom Out = 0 and  1

End Type

All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types. Make sure to paste the BITMAP.GLO file in your global module. Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBMP function before ending your program, or the bitmap will stay in memory. It is a good idea to place DeleteBmp calls in your Form_Unload event procedure.

Example:

The following example will set the bits of a bitmap into the Bits() array.

 Call Bitmap2DIB (Bmp)

 '-- Bits() now contains the DIB bits data of Bmp.hBitmap


Bmp2Clip

Visual Basic Subroutine contained in BITMAP.BAS

Purpose:

Bmp2Clip makes a copy of a bitmap and palette and places them into the clipboard.

Syntax:

Call Bmp2Clip (Bmp)

Where:

Bmp is a BMPTYPE type variable that contains the bitmap information

Comments:

The BMPTYPE structure is as follows:

       Type BMPTYPE

 File As BITMAPFILEHEADER 	'-- 14 Bytes

 Info As BITMAPINFOHEADER 	'-- 40 Bytes

 BInfo As BITMAPINFO 	'-- 1064 Bytes

 hBitmap As Integer 	'-- Handle to the bitmap

 hPalette As Integer 	'-- Handle to the palette

 FileName As String * 50 	'-- BMP file name (for your use)

 hWnd As Integer  	'-- Handle to display window

 Top As Integer 	'-- Top pixel position inside window

 Left As Integer  	'-- Left pixel position inside window

 ZoomFactor As Single 	'-- Zoom In = 1, Zoom Out = 0  and 1

       End Type

All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types. Make sure to paste the BITMAP.GLO file in your global module. Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBmp function before ending your program, or the bitmap will stay in memory. A good idea is to place DeleteBmp calls in your Form_Unload event procedure.

Example:

The following example will Capture the client area of Form1 and copy it to the clipboard:

 Dim Cap As BmpType

 Dim Area As Rect

 hW = Form1.hWnd

 Call CaptureBmp(hW, Area, Cap)

 Call Bmp2Clip(Cap)

 Call DeleteBmp(Cap)


BoxControlOnControl

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

The BoxControlOnControl routine is used to provide a three dimensional box effect around a control which is placed on another control, usually a picture box.

Syntax:

BoxControlOnControl CtlName, Container, BWidth%, BoxEffect%,_

LineWidth%

Where:

CtlName is the name of the control to box

Container is container of the control (i.e., PictureBox, GroupBox, etc.)

BWidth% is the distance in pixels from the edge of the control

BoxEffect% is the box effect as described below, and LineWidth% is the line width in pixels

Comments:

BoxEffect% will be one of the following constants:

CONSTANT VALUE	DESCRIPTION

  0	BOX_RAISED

Creates the appearance of a raised surface around the

control.

 1	BOX_SUNKEN

Creates the appearance of a sunken surface around the control.

 3	BOX_RAISEDLINE

Creates the appearance of a raised line around the control.

 2	BOX_SUNKENLINE

Creates the appearance of a sunken line around the control.

 4	BOX_SHADOWED

Creates the appearance of a shadow along the right and bottom

edges of the control.

 6	BOX_BEVELED

Creates a beveled edge border around the control.

The LineWidth% parameter is ignored when using the BOX_SHADOWED and BOX_BEVELED constant.

The BWidth% parameter is ignored when using the BOX_BEVELED constant.

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before BoxControlOnControl can be used.


BoxControlOnForm

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

The BoxControlOnForm routine is used to provide a three dimensional box effect around a control, which is placed directly on a form.

Syntax:

BoxControlOnForm CtlName, BWidth%, BoxEffect%, LineWidth%

Where:

CtlName is the name of the control to box

BWidth% is distance the in pixels from edge of control

BoxEffect% is the box effect as described below, and LineWidth% is the line width in pixels

Comments:

BoxEffect% will be one of the following constants:

CONSTANT VALUE	DESCRIPTION

 0	BOX_RAISED

Creates the appearance of a raised surface around the control.

 1	BOX_SUNKEN

Creates the appearance of a sunken surface around the control.

 3	BOX_RAISEDLINE

Creates the appearance of a raised line around the control.

 2	BOX_SUNKENLINE

Creates the appearance of a sunken line around the control.

 4	BOX_SHADOWED

Creates the appearance of a shadow along the right and bottom

edges of the control.

 6	BOX_BEVELED

Creates a beveled edge border around the control.

The LineWidth% parameter is ignored when using the BOX_SHADOWED and BOX_BEVELED constants.

The BWidth% parameter is ignored when using the BOX_BEVELED constant.

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before BoxControlOnForm can be used.


BoxDrawControl

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

The BoxDrawControl routine is used to draw three dimensional boxes on picture boxes based on pixel coordinates. The pixel count is based on screens with 15 twips per pixel. The pixel count is automatically adjusted, by BoxDrawControl, for monitors that have 12 twips per pixel.

Syntax:

BoxDrawControl Picture, BoxEffect%, LineWidth%, drawRect

Where:

Picture is the name of the control on which to draw the box

BoxEffect% is the box effect as described below, and LineWidth% is the line width in pixels

drawRect is a structure of type RECT that specifies where to draw the box

Comments:

 BoxEffect% will be one of the following constants:

CONSTANT VALUE	DESCRIPTION

BOX_RAISED

 0	Creates the appearance of a raised surface around the control.

 1	BOX_SUNKEN

Creates the appearance of a sunken surface around the control.

 3	BOX_RAISEDLINE

Creates the appearance of a raised line around the control.

 2	BOX_SUNKENLINE

Creates the appearance of a sunken line around the control.

 4	BOX_SHADOWED

Creates the appearance of a shadow along the right and bottom

edges of the control.

 6	BOX_BEVELED

Creates a beveled edge border around the control.

The LineWidth% parameter is ignored when using the BOX_SHADOWED and BOX_BEVELED constants.

The RECT structure is defined in 3DFX.BAS.

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before BoxDrawControl can be used.


BoxDrawForm

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

The BoxDrawForm routine is used to draw three dimensional boxed on forms based on pixel coordinates.  The pixel count is based on screens with 15 twips per pixel.  The pixel count is automatically adjusted, by BoxDrawForm, for monitors that have 12 twips per pixel.

Syntax:

BoxDrawForm FormName, BoxEffect%, LineWidth%, drawRect

Where:

FormName is the name of the form on which to draw the box.

BoxEffect% is the box effect and LineWidth% is the line width in pixels.

drawRect is a struture of type RECT that specifies where to draw the box.

Comments:

BoxEffect% will be one of the following constants:

CONSTANT VALUE	DESCRIPTION

BOX_RAISED

 0	Creates the appearance of a raised surface around the control.

 1	BOX_SUNKEN

Creates the appearance of a sunken surface around the control.

 3	BOX_RAISEDLINE

Creates the appearance of a raised line around the control.

 2	BOX_SUNKENLINE

Creates the appearance of a sunken line around the control.

 4	BOX_SHADOWED

Creates the appearance of a shadow along the right and bottom

edges of the control.

 6	BOX_BEVELED

Creates a beveled edge border around the control.

The LineWidth% parameter is ignored when using the BOX_SHADOWED and BOX_BEVELED constants.

The RECT structure is defined in 3DFX.BAS.

The BWidth% parameter is ignored when using the BOX_BEVELED constant.

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before BoxDrawForm can be used.


CaptureBmp

Visual Basic Subroutine contained in BITMAP.BAS

Purpose:

CaptureBmp "captures" any portion of any visible window, or the entire screen and returns it in a BMPTYPE structure.

Syntax:

Call CaptureBmp (hWnd, Area, Bmp)

Where:

hWnd is a valid handle to a window

Area is a RECT structure that contains the rectangular area to capture

Bmp is a type variable of type BMPTYPE

Comments:

Setting hWnd to 0 tells CaptureBmp to capture from the entire screen.

The Area variable must be dimensioned as a RECT structure, as follows:

Type RECT

 Left As Integer

 Top As Integer

 Right As Integer

 Bottom As Integer

End Type

Dim Area As Rect

The Left, Top, Right, and Bottom values must be in Pixel units, not in Twips.

If Area.Right or Area.Bottom are set to 0, the width of the client area is assumed.

Once the area is captured into the BMPTYPE variable, it can be displayed, saved, or manipulated just as if it were loaded from a file.

The BMPTYPE structure is as follows:

     Type BMPTYPE

 File As BITMAPFILEHEADER 	'-- 14 Bytes

 Info As BITMAPINFOHEADER 	'-- 40 Bytes

 BInfo As BITMAPINFO 	'-- 1064 Bytes

 hBitmap As Integer 	'-- Handle to the bitmap

 hPalette As Integer 	'-- Handle to the palette

 FileName As String * 50 	'-- BMP file name (for your use)

 hWnd As Integer  	'-- Handle to display window

 Top As Integer  	'-- Top pixel position inside window

 Left As Integer  	'-- Left pixel position inside window

 ZoomFactor As Single 	'-- Zoom In = 1, Zoom Out = 0  and 1

     End Type

All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types. Make sure to paste the BITMAP.GLO file in your global module. Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBmp function before ending your program, or the bitmap will stay in memory. A good idea is to place DeleteBmp calls in your Form_Unload event procedure.

Example:

The following example will Capture the client area of Form1 to a BMPTYPE variable and save it to a file called CAPTURE.BMP:

 Dim Bmp As BmpType

 Dim Area As Rect

 hW = Form1.hWnd

 Call CaptureBmp(hW, Area, Bmp)

 Call SaveBmp("CAPTURE.BMP", Bmp)

 Call DeleteBmp(Bmp)


CBFindString

assembler function contained in QPRO200.DLL

Purpose:

Searches a Combo Box (Visual Basic) for a matching string and returns the index of the match.

Declare Syntax:

Declare Function CBFindString% Lib "QPRO200.DLL" (Combo As Control, _

  ByVal Start%, ByVal Search$, ByVal Selection%)

Call Syntax:

Found% = CBFindString%(ComboName, Start%, Search$, Selection%)

Where:

ComboName is the combo box you wish to search.  Start% is the starting index from which to start the search.  Use -1 to indicate that the search should start from the beginning of the list.

Search$ is the string or substring to search for, and since searching is not case sensitive you don't have to worry about its capitalization.  Select% can be set on entry to either True (-1) or False (0) to specify whether or not the index that is found should become the current selection.

Found% is the result of the search, and it reflects the offset from start of the first match or -1 to indicate that no match was found.


CB2Array and List2Array

assembler routines contained in QPRO200.DLL

Purpose:

These routines fill a conventional string array with data from a list or combo box.

Declare Syntax:

Declare Sub CB2Array Lib "QPRO200.DLL" (Combo As Control, _

Array$, ByVal NumEls%)

Declare Sub List2Array Lib "QPRO200.DLL" (List As Control, _

Array$, ByVal NumEls%)

Call Syntax:

CB2Array ComboName, Array$(Start), NumEls%

List2Array ListName, Array$(Start), NumEls%

Where:

ComboName and ListName are the name of a combo box or list box control.  Array$(Start) is the first element of an array to be filled with the control's data.  NumEls% specifies the number of elements to fill.

Comments:

Both routines require that the string array be dimensioned to the number of elements in the List or Combo Box as follows:

NumEls% = List1.ListCount

ReDim Array$(NumEls%)

List2Array List1, Array$(0), NumEls%

List2Array and CB2Array will resize each element in the string array to the length of each corresponding element in the List/Combo Box, eliminating the need to pre-pad the string array.

Note that List2Array can also be used with the QPList custom list box control.

Also See:

Also see CB2File and List2File which save the contents of a combo or list box to a sequential file.


CB2File and List2File

assembler routines contained in QPRO200.DLL

Purpose:

These routines save the contents of a combo or list box to a sequential file.

Declare Syntax:

Declare Sub CB2File Lib "QPRO200.DLL" (Combo as Control,ByVal _

FileName$, ByVal ModeFlag%)

Declare Sub List2File Lib "QPRO200.DLL" (List as Control, ByVal _

FileName$, ByVal ModeFlag%)

Call Syntax:

CB2File ComboName, FileName$, ModeFlag%

List2File ListName, FileName$, ModeFlag%

Where:

ComboName and ListName are the name of a combo box or list box control.  FileName$ is the name of the file that will receive the control's data, and ModeFlag% specifies whether the file is created or appended to.

Comments:

List2File and CB2File replace or create the file when the ModeFlag parameter is set to 0, or append the strings to the end if set to -1.

Note that List2Array can also be used with the QPList custom list box control.

Also See:

Also see CB2File and List2File which save the contents of a combo or list box to a sequential file.


Clip2Bmp

Visual Basic Subroutine contained in BITMAP.BAS

Purpose:

Clip2Bmp grabs a bitmap and palette from the clipboard and sets them into a BMPTYPE variable.

Syntax:

Call Clip2Bmp (Bmp)

Where:

Bmp is a type variable of type BMPTYPE.

Comments:

The BMPTYPE structure is as follows:

Type BMPTYPE

 File As BITMAPFILEHEADER 	'-- 14 Bytes

 Info As BITMAPINFOHEADER 	'-- 40 Bytes

 BInfo As BITMAPINFO 	'-- 1064 Bytes

 hBitmap As Integer 	'-- Handle to the bitmap

 hPalette As Integer 	'-- Handle to the palette

 FileName As String * 50 	'-- BMP file name (for your use)

 hWnd As Integer  	'-- Handle to display window

 Top As Integer  	'-- Top pixel position inside window

 Left As Integer  	'-- Left pixel position inside window

 ZoomFactor As Single 	'-- Zoom In = 1, Zoom Out = 0  and 1

End Type

All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types. Make sure to paste the BITMAP.GLO file in your global module. Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBmp function before ending your program, or the bitmap will stay in memory. A good idea is to place DeleteBmp calls in your Form_Unload event procedure.

Example:

The following example will copy a bitmap and palette from the clipboard to a BMPTYPE variable, display it on Form1, and save it to disk.

 Dim Bmp As BmpType

 Call Clip2Bmp(Bmp)

 Bmp.hWnd = Form1.hWnd

 Call DisplayBmp(Bmp)

 FileName$ = "C:\CAPTURE.BMP"

 Call SaveBmp(FileName$, Bmp)

Example Location:

Project File	Form/BASIC File	Procedure

DEMOBMP.MAK	DEMOBMP.BAS	GetAndDisplay


ControlHWnd

assembler function contained in QPRO200.DLL

Purpose:

ControlHWnd returns the Windows handle (hWnd) of a given control.

Declare Syntax:

Declare Function ControlHWnd% Lib "QPRO200.DLL" (Ctrl As Control)

Call Syntax:

WinHandle% = ControlHWnd%(Text1)

Where:

WinHandle% receives the Windows handle of the Text1 control.

Comments:

While Visual Basic has a method for determining the handle of a form (FormName.hWnd), there is no similar method for a control. If you need to pass a control's handle to a Windows API routine, this is the most direct way.

Example Location:

Project File	Form/BASIC File	Procedure

READDIRS.MAK	READDIRS.FRM	GoStop_Click


CopyBmp

Visual Basic Subroutine contained in BITMAP.BAS

Purpose:

CopyBmp copies one BMPTYPE variable to another. CopyBmp only copies the data in the variable, not the bitmap data itself.

Syntax:

Call CopyBmp (Source, Dest)

Where:

Source is the source BMPTYPE variable

Dest is the destination BMPTYPE variable

Comments:

NOTE:

CopyBmp does NOT make a copy of the actual bitmap (and palette).  It copies one BMPTYPE variable to another BMPTYPE variable.  Two or more BMPTYPE variables can "point to" the same bitmap. If you delete one of these variables with DeleteBmp, all of the other BMPTYPE variables will become invalid, because they all point to the bitmap that was deleted.

The BMPTYPE structure is as follows:

Type BMPTYPE

 File As BITMAPFILEHEADER 	'-- 14 Bytes

 Info As BITMAPINFOHEADER 	'-- 40 Bytes

 BInfo As BITMAPINFO 	'-- 1064 Bytes

 hBitmap As Integer 	'-- Handle to the bitmap

 hPalette As Integer 	'-- Handle to the palette

 FileName As String * 50 	'-- BMP file name (for your use)

 hWnd As Integer  	'-- Handle to display window

 Top As Integer  	'-- Top pixel position inside window

 Left As Integer  	'-- Left pixel position inside  window

 ZoomFactor As Single 	'-- Zoom In = 1, Zoom Out = 0  and 1

End Type

All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types. Make sure to paste the BITMAP.GLO file in your global module. Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBmp function before ending your program, or the bitmap will stay in memory. A good idea is to place DeleteBmp calls in your Form_Unload event procedure.


CSCircle

assembler subprogram contained in CSPICTUR.VBX

Purpose:

Draws a circle, ellipse, or arc on a CSPicture custom control.  This routine is functionally equivalent to the standard Visual Basic Circle method.

Declare Syntax:

Declare Sub csCircle Lib "CSPICTUR.VBX" (Ctrl As Control, ByVal _

sStep%, ByVal X!, ByVal Y!, ByVal Radius!, ByVal Clr&, ByVal_

AStart!, ByVal AEnd!, ByVal Aspect!)

Call Syntax:

csCircle csPicture1, sStep%, X!, Y!, Radius!, Clr&, AStart!, _

AEnd!,Aspect!

Comments:

Please see the Visual Basic help on the Circle Method for more complete information.  The csCircle sub is functionally identical to the standard Visual Basic Circle method with the following exceptions:

1)	csCircle does not accept an optional number of parameters.

2)	csCircle is dedicated to the csPicture Custom control.

3)	Since 'Step' is a Basic keyword the boolean csStep should be set to -1 if

the Step action is desired, 0 if it is not.  The constants CS_STEP or

NO_STEP defined in QPRODECL.BAS may also be used.

4)	Clr& will not assume the ForeColor value unless it is set.

5)	start!, end!, aspect! are functionally the same.

6)	Filling a closed region is functionally the same.

7)	The DrawWidth property is not currently supported.

8)	The DrawMode property is not currently supported.

When csCircle is executed, CurrentX and CurrentY are set to the center point specified by the arguments.  This is functionally the same as the Visual Basic Circle method.

csCircle may be declared as a Function, if so, it simply returns 0 if an error occurred, non-zero if successful.

See Also:

CurrentX, CurrentY, DrawMode, DrawWidth, FillColor, FillStyle, ForeColor, QBColor, RGB


CSEnumFonts

assembler function contained in QPRO200.DLL

Purpose:

CSEnumFonts Returns the number of and information about fonts loaded into the system.

Declare Syntax:

Declare Function CSEnumFonts Lib "QPRO200.DLL" (ByVal Flag%, ByVal_

hDC%, ByVal FaceName as Any, FTArray%, LogFontArray as _

LogFont,TextMetArray as TextMetrics)

Call Syntax:

NumFonts% = CSEnumFonts (Flag%, hDC%, FaceName$, Ftarray%(0),_

LogFontArray(0), TextmetArray(0))

Where:

Flag% = 0     CSEnumFonts counts fonts only.

Flag% = 1     CSEnumFonts fills arrays specified

hDC% is either 0 to use Desktop hDC, or an hDC specified.

FaceName$ is a facename to use, Null for any.

FTArray%() is an integer array to receive FontType flags.

LogFontArray() is an array of LogFont type structures to receive data.

TextMetArray() is an array of TextMetric type structures to receive data.

LogFont and TextMetric types are Windows structures that contain information about the

fonts.

Comments:

CSEnumFonts requires that the following lines be added to your Global module.  These are also listed in DEMOFONT.GLO:

Type LOGFONT

 lfHeight As Integer

 lfWidth As Integer

 lfEscapement As Integer

 lfOrientation As Integer

 lfWeight As Integer

 lfItalic As String * 1

 lfUnderline As String * 1

 lfStrikeOut As String * 1

 lfCharSet As String * 1

 lfOutPrecision As String * 1

 lfClipPrecision As String * 1

 lfQuality As String * 1

 lfPitchAndFamily As String * 1

 lfFaceName As String * 32

End Type

Type TEXTMETRIC

 tmHeight As Integer

 tmAscent As Integer

 tmDescent As Integer

 tmInternalLeading As Integer

 tmExternalLeading As Integer

 tmAveCharWidth As Integer

 tmMaxCharWidth As Integer

 tmWeight As Integer

 tmItalic As String * 1

 tmUnderlined As String * 1

 tmStruckOut As String * 1

 tmFirstChar As String * 1

 tmLastChar As String * 1

 tmDefaultChar As String * 1

 tmBreakChar As String * 1

 tmPitchAndFamily As String * 1

 tmCharSet As String * 1

 tmOverhang As Integer

 tmDigitizedAspectX As Integer

 tmDigitizedAspectY As Integer

End Type

Example:

NumFonts% = CSEnumFonts%(0, 0, 0&, LFArray(0), TMArray(0), _

FontTypes%(0))	‘simply count the fonts

Redim LFArray(NumFonts%) As LogFont	'redim arrays to Numfonts

Redim TMArray(NumFonts%) As TextMetrics 'receive fonts

Redim FontTypes%(NumFonts%)	’receive font types

NumFonts% = CSEnumFonts%(1, 0, 0&, LFArray(0), TMArray(0),_

FontTypes%(0))	'fill the arrays with the  font information

Example Location:

Project File	Form/BASIC File	Procedure

DEMOFONT.MAK	DEMOFONT.FRM	GetFontNames

NOTE:

The FTArray() contains values built up of the following masks:

RASTER_FONTTYPE = 1

DEVICE_FONTTYPE = 2

TRUETYPE_FONTTYPE = 4


CSLine

assembler subprogram contained in CSPICTUR.VBX

Purpose:

Draws a Line, Box, or filled Box on a CSPicture custom control.  This routine is functionally equivalent to the standard Visual Basic Line method.

Declare Syntax:

Declare Sub csLine LIB "CSPICTUR.VBX" (Ctrl As Control, ByVal _

sSTEP1%, ByVal X1!, ByVal Y1!, ByVal sSTEP2%, ByVal X2!, ByVal _

Y2!, ByVal Clr&, ByVal BoxFill$)

Call Syntax:

csLine csPicture1, sStep1%, X1!, Y1!, sStep2%, X2!, Y2!, Clr&, _

["" | "B" | "BF"]

Comments:

Please see the Visual Basic documentation or help on the Line Method for more complete information.

The csLine sub is functionally identical to the Line method with the following exceptions:

1)	csLine does not accept an optional number of parameters.

2)	csLine is dedicated to the csPicture Custom control.

3)	Since 'Step' is a Basic keyword the boolean sStep should be set to -1 if the Step

action is desired, 0 if it is not.  The constants CS_STEP or NO_STEP defined in

QPRODECL.BAS may also be used.

4)	Clr& will not assume the ForeColor value unless it is set.

5)	The BoxFill$ has only 3 valid arguments, They are "" (NULL), "B" (upper case)

and "BF" (upper case).

6)	The DrawWidth property is not currently supported.

7)	The DrawMode property is not currently supported.

When csLine is executed, CurrentX and CurrentY are set to (X2!, Y2!) specified by the arguments.  No distinction is made for Boxes.

csLine may be declared as a Function, if so, it simply returns 0 if an error occurred, non-zero if successful.

Also See the following Visual Basic functions:

CurrentX, CurrentY, DrawMode, DrawWidth, FillColor, FillStyle, ForeColor, QBColor, RGB


CSPrint

assembler sub program contained in CSPICTUR.VBX

Purpose:

Prints a string on a CSPicture custom control.  This routine is functionally equivalent to the standard Visual Basic Print method.

Declare Syntax:

Declare Sub csPrint Lib "CSPICTUR.VBX" (Ctrl As Control, ByVal _

Text$, ByVal  EolAction%)

Call Syntax:

csPrint csPicture1, Text$, EolAction%

Comments:

Please see the Visual Basic documentation or help on the Print method for more complete information.  The csPrint sub is functionally equivalent to the Print method with the following exceptions:

1)	csPrint does not accept an optional number of parameters.

2)	csPrint is dedicated to the csPicture Custom control.

3)	Only Text Strings may be printed.  To print numeric values use Visual Basic Str$

function.

EolAction currently has 2 modes:

VALUE:	BEHAVIOR:

0	Like a plain Print statement that appends the carriage return line feed to

the text locating the CurrentX and CurrentY properties at the left edge of

the next line.

1	Like a Print statement followed with a semicolon. The CurrentX property

is incremented by the extent of the text$. The CurrentY property is

unchanged.

Other	Unrecognized values are treated like 0.

In all cases Text$ will be printed in the current ForeColor & BackColor of the control's property values.


CtlFrame

assembler subroutine contained in QPRO200.DLL

Purpose:

Puts a pop, drop or plain frame around a Visual Basic or custom control.

Declare Syntax:

Declare Sub CtlFrame Lib "QPRO200.DLL" (Name as Control,ByVal_

FrameEffect%, ByVal SunColor&, ByVal ShadowColor&,ByVal _

BorderWidth%,ByVal FrameStyle%)

Call Syntax:

CtlFrame List1, FrameEffect%, SunColor&, ShadowColor&, BorderWidth%,_

FrameStyle%

Where:

Name is the name of a control around which to draw the frame.  CtlFrame uses the Left, Top, Width and Height properties to place the frame around the control.  CtlFrame gets the parent of the control and uses the hDC of the parent to paint on.  The parent usually is the Form the control is on, but can also be a picture box or another control.

FrameEffect tells CtlFrame which effect (off, pop, drop, plain) to use in painting the frame.

0 = None

1 = Pop

2 = Drop

3 = Plain

SunColor specifies the color of the highlight side on a Pop or drop frame, and the overall color of a plain frame.  It can be set to any standard Visual Basic color value as a long integer.

ShadowColor specifies the color of the shadow side of a Pop or Drop frame.  It can be set to any standard Visual Basic color value as a long integer.

BevelWidth specifies the width of a Pop, Drop, or Plain frame in pixels.

BorderStyle specifies whether a black line is painted inside the specified frame.

0 = None

1 = Single Line

Comments:

CtlFrame is best placed in the Paint event of its parent to guarantee that the frame gets repainted if the control is moved, changed or revealed.

TIP:  If; however, the parent's AutoRedraw property is set to TRUE, you should instead put calls to CtlFrame in the form's Load event since the Paint event is never called in this case.


DeleteBmp

Visual Basic Subroutine contained in BITMAP.BAS

Purpose:

DeleteBmp deletes a bitmap (and it's palette) from memory.

Syntax:

Call DeleteBmp (Bmp)

Where:

Bmp is a BMPTYPE type variable that contains the bitmap information.

Comments:

The BMPTYPE structure is as follows:

Type BMPTYPE

  File As BITMAPFILEHEADER 	'-- 14 Bytes

  Info As BITMAPINFOHEADER 	'-- 40 Bytes

  BInfo As BITMAPINFO 	'-- 1064 Bytes

  hBitmap As Integer 	'-- Handle to the bitmap

  hPalette As Integer 	'-- Handle to the palette

  FileName As String * 50 	'-- BMP file name (for your use)

  hWnd As Integer  	'-- Handle to display window

  Top As Integer  	'-- Top pixel position inside window

  Left As Integer  	'-- Left pixel position inside window

  ZoomFactor As Single 	'-- Zoom In = 1, Zoom Out = 0  and 1

End Type

All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types. Make sure to paste the BITMAP.GLO file in your global module.  Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBmp function before ending your program, or the bitmap will stay in memory.  It is a good idea to place DeleteBmp calls in your Form_Unload event procedure.


DepthFrameCtrl

BASIC Subprogram contained in FRAMES.BAS

Purpose:

DepthFrameCtrl is used to draw a frame around an area within a Picture Box.

Syntax:

Dim DF As DepthFrame 	'Create a Type for parameters

DF.UlColor = 8421504 	'Top & Left color (White)

DF.BrColor = 16777215 	'Bottom & Right color (Grey)

DF.UlX = 0        	'Upper Left X coordinate

DF.UlY = 0   	'Upper Left Y coordinate

DF.BrX = 1500   	'Bottom Right X coordinate

DF.BrY = 1500   	'Bottom Right Y coordinate

DF.XThickness = 15   	'Left and Right thickness

DF.YThickness = 15   	'Top and Bottom thickness

DepthFrameCtrl CtrlName, DF	'Specify the name of the Picture Box.

Where:

CtrlName is the name of a picture box on the current form where the frame will be drawn.  DF is a "DepthFrame" Type variable which is defined in PRODECL.BAS.

Comments:

DepthFrameCtrl is useful for drawing frames within Picture Boxes that give the illusion of depth (concave/convex).

The .UlColor, and .BrColor fields of the DepthFrame type are used to specify contrasting colors for the top and left sides of the frame, as well as the bottom and right sides respectively.  If the background color of the picture box is light grey you could specify dark grey for the top and left sides, and white for the bottom and right sides.  This gives the illusion of a sunken area within the frame.

The .XThickness and .YThickness fields are used to specify the thickness of the frame, thus controlling the perceived depth of the area within the frame.  The thicknesses specified should be in whatever units are in effect for the control on which the frame is drawn.  If Twips are in effect, values should be given as multiples of 15, which roughly correspond to pixels on most monitors.

Typically you would call this routine from a Picture Box's Paint procedure, if its AutoRedraw property is set to False.

If AutoRedraw is True the call should instead be placed in the Form's Load procedure, since the Paint procedure is never invoked in this case.

DepthFrameCtrl is used in the Form_Load procedure within the QP4WIN.FRM module.

See Also:

DepthFrameFrm PaintFrameCtrl and PaintFrameFrm.

Example Location:

Project File	Form/BASIC File	Procedure

QP4WIN.MAK	QP4WIN.FRM	Form_Load


DepthFrameFrm

BASIC Subprogram contained in FRAMES.BAS

Purpose:

DepthFrameFrm is used to draw a frame around an area within a Form.

Syntax:

Dim DF As DepthFrame	'Create a Type for parameters

DF.UlColor = 8421504	‘Top & Left color (White)

DF.BrColor = 16777215	'Bottom & Right color (Grey)

DF.UlX = 0	'Upper Left X coordinates

DF.UlY = 0	'Upper Left Y coordinates

DF.BrX = 1500	'Bottom Right X coordinates

DF.BrY = 1500	'Bottom Right Y coordinates

DF.XThickness = 15	'Left and Right thickness

DF.YThickness = 15	'Top and Bottom thickness

DepthFrameFrm FormName, DF	'Specify the name of the

Picture Box.

Where:

FormName is the name of the form on which to draw the frame.  DF is a "DepthFrame" Type variable which is defined in PRODECL.BAS.

Comments:

DepthFrameForm is useful for drawing frames within a form that give the illusion of depth (concave/convex).

The .UlColor and .BrColor fields of the DepthFrame type are used to specify contrasting colors for the top and left sides of  the frame, as well as the bottom and right sides respectively.  If the background color of the form is light grey you could specify dark grey for the top and left sides, and white for the bottom and right sides.  This gives the illusion of a sunken area within the frame.

The .XThickness and .YThickness fields specify the thickness of the frame, thus controlling the perceived depth of the area within the frame.  The thicknesses specified should be in whatever units are in effect for the control on which the frame is drawn.  If Twips are in effect values should be given as multiples of 15, which roughly correspond to pixels on most monitors.

Typically you would call this routine from a Form's Paint procedure, if its AutoRedraw property is set to False.  If AutoRedraw is True the call should instead be placed in the Form's Load, procedure since the Paint procedure is never invoked in this case.

See Also:

DepthFrameCtrl PaintFrameCtrl and PaintFrameFrm.


DisplayBmp

Visual Basic Function contained in BITMAP.BAS

Purpose:

DisplayBmp displays a BMP file that was loaded with LoadBmp

Syntax:

Call DisplayBmp (Bmp)

Where:

Bmp is a BMPTYPE type variable that contains the bitmap information

Comments:

You control how the image is displayed by setting the values in the BMPTYPE structure. The elements you can set are:

hWnd	'-- The target window

Top	'-- Top row pixel position

Left	'-- Left column pixel position

ZoomFactor	'-- Zoom in or out

You must tell DisplayBmp where to display the image by setting hWnd to a valid window handle prior to calling DisplayBmp. See the example below.

DisplayBmp will display a bitmap in any window, including buttons, list boxes, picture controls, etc. Any window that you can get an hWnd of (use ControlHWnd to get a handle to a control's window) is fair game for DisplayBmp. You can display a bitmap on the entire

screen (covering everything else) by setting the hWnd to 0.

The Top and Left variables relate to where inside the target window to display the image. Note: these are Pixel values, not Twip values, unlike Visual Basic's Left and Top form properties.

You can display a loaded image at any size by setting ZoomFactor to any non-negative, non-zero value. To zoom in (enlarge) the image, set ZoomFactor to a value greater than 1. To zoom out (shrink) the image, set ZoomFactor to any value between 0 and 1. See the example below.All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types. Make sure to paste the  BITMAP.GLO file in your global module. Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBmp function before ending your program, or the bitmap will stay in memory. A good idea is to place DeleteBmp calls in your Form_Unload event procedure.

The BMPTYPE structure is as follows:

Type BMPTYPE

File As BITMAPFILEHEADER 	'-- 14 Bytes

Info As BITMAPINFOHEADER 	'-- 40 Bytes

BInfo As BITMAPINFO 	'-- 1064 Bytes

hBitmap As Integer	'-- Handle to the bitmap

hPalette As Integer	'-- Handle to the palette

FileName As String * 50	'-- BMP file name (for your use)

hWnd As Integer	'-- Handle to display window

Top As Integer	'-- Top pixel position inside window

Left As Integer	'-- Left pixel position inside window

ZoomFactor As Single	'-- Zoom In = 1, Zoom Out = 0  and  1

End Type

All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types. Make sure to paste the BITMAP.GLO file in your global module. Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBmp function before ending your program, or the bitmap will stay in memory. A good idea is to place DeleteBmp calls in your Form_Unload event procedure.

Examples:

The following example will load and display a bitmap file on Form1:

Dim Bmp as BmpType

FileName$ = "C:\WINDOWS\ARCADE.BMP"

Call LoadBmp(FileName$, Bmp)

Bmp.hWnd = Form1.hWnd

Call DisplayBmp(Bmp)

The following example displays a loaded bitmap at 2.5 times normal size on the screen:

Bmp.hWnd = Form1.0

Bmp.ZoomFactor = 2.5

Call DisplayBmp(Bmp)

Example Location:

Project File	Form/BASIC File	Procedure

DEMOBMP.MAK	DEMOBMP.BAS	GetAndDisplay


DoCheckBox

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

With the DoCheckBox routine you can simulate a three dimensional check box in one of various styles by using a normal check box in conjunction with a small bitmap. The bitmap covers the box portion of the check box control. This routine copies the appropriate bitmap onto the picture depending on the state of the actual check box. Use this routine in conjunction with the "PassPicClickToCheck" routine, which passes mouse clicks on the bitmap onto the actual underlying check box.

Syntax:

DoCheckBox ChkValue%, picSrc, picDest, BoxStyle%, CheckStyle%

Where:

ChkValue% specifies whether or not to add the check bitmap onto the bitmap of the three dimensional check box (should be '0' or '1'. The 'gray' state is not supported.).

picSrc is the name of the picture box that contains the provided "3dboxch.bmp" picture (The picture should be accessible from anywhere in the project, preferably placed in the startup form or an invisible form that remains loaded at all times.).

picDest is the name of the picture box to receive the picture.

BoxStyle% is the style of the checkbox and CheckStyle% is the style of checkmark.

Comments:

BoxStyle% is one of the following: CHK_BOXSUNKEN (1), HK_BOXRAISED(0), CHK_DIAMONDSUNKEN (49), CHK_DIAMONDRAISED(73). CheckStyle% is one of the following: CHK_BLUECHECK(97), CHK_REDCHECK(121), CHK_SMALLX(145), CHK_LARGEX(167).

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before DoCheckBox can be used.


DoControlLabelEmbossed

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

The DoControlLabelEmbossed routine is used to create an "embossed" effect using ordinary lightweight label controls which are placed on a picture box.

Syntax:

DoControlLabelEmbossed picContainer, L1, L2, L3, Text$, Effect%

Where:

picContainer is the picture box which contains the three label controls.

L1 is the "real" label, L2/3 are shadow label controls.

Text$ is the caption (NULL if the routine should use the text from L1), and Effect% is the

effect as indicated in the comments below.

Comments:

First create three labels and place them on the picture. The first label will be the "real" label.   The second and third labels provide the embossed effect. All labels must have their "BackStyle" property set to 0 - transparent. It is usually simplest to create a three-element control array, and use label1(0) as the real label, and label1(1) and label1(2) as the shadow labels. Either put the actual caption on the first label, or use dummy text as a placeholder, and set the caption through this function.

Effect% will be one of the following: LBL_STANDARD(0), LBL_RAISED(1), LBL_SUNKEN(2).

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before DoControlLabelEmbossed can be used.


DoFormLabelEmbossed

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

The DoFormLabelEmbossed routine is used to create an "embossed" effect using ordinary lightweight label controls which are placed on a form.

Syntax:

DoFormLabelEmbossed L1, L2, L3, Text$, Effect%

Where:

L1 is the "real" label, L2/3 are shadow label controls.

Text$ is the caption (NULL if the routine should use the text from L1), and Effect% is the

effect as indicated in the comments below.

Comments:

First create three labels and place them on the form. The first label will be the "real" label. The second and third labels provide the embossed effect. All labels must have their "BackStyle" property set to 0 - transparent. It is usually simplest to create a three-element control array, and use label1(0) as the real label, and label1(1) and label1(2) as the shadow labels. Either put the actual caption on the first label, or use dummy text as a placeholder, and set the caption through this function.

Effect% will be one of the following: LBL_STANDARD(0), LBL_RAISED(1), LBL_SUNKEN(2).

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before DoFormLabelEmbossed can be used.


DoLabelOnControl

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

This routine is used to create a normal or three dimensional effect with standard labels which are placed on containers such as picture boxes.

Syntax:

DoLabelOnControl ctlContainer, L1, L2, Text$, Effect%

Where:

ctlContainer is the container control (i.e., PictureBox, GroupBox, etc.).

L1 is the "real" label.

L2 is the shadow control.

Text$ is the caption (NULL if L1 caption should be used), and Effect% is the text effect as

indicated in the comments below.

Comments:

First create two labels and place them on the container. The first label will be the "real" label.   The second label provides the three dimensional effect. Both labels must have their "BackStyle" property set to 0 - transparent. It is usually simplest to create a two-element control array, and use label1(0) as the real label, and label1(1) as the shadow label. Either put the actual caption on the first label, or use dummy text as a placeholder, and set the caption through this function.

Effect% will be one of the following: LBL_STANDARD(0), LBL_RAISED(1), LBL_SUNKEN(2).

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before DoLabelOnControl can be used.


DoLabelOnForm

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

This routine is used to create a normal or three dimensional effect with standard labels which are placed on directly on forms.

Syntax:

DoLabelOnControl L1, L2, Text$, Effect%

Where:

L1 is the "real" label.

L2 is the shadow control.

Text$ is the caption (NULL if L1 caption should be used), and Effect% is the text effect as

indicated in the comments below.

Comments:

First create two labels and place them on the form. The first label will be the "real" label. The second label provides the three dimensional effect. Both labels must have their "BackStyle" property set to 0 - transparent. It is usually simplest to create a two-element control array, and use label1(0) as the real label, and label1(1) as the shadow label. Either put the actual caption on the first label, or use dummy text as a placeholder, and set the caption through this function.

Effect% will be one of the following: LBL_STANDARD(0), LBL_RAISED(1), LBL_SUNKEN(2).

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before DoLabelOnForm can be used.


DoOptionButton

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

With this routine you can simulate a three dimensional option button by using a normal option button in conjunction with a small bitmap.

Syntax:

DoOptionButton chkValue%, picSrc, picDest, Style%

Where:

chkValue% specifies whether or not to add the check bitmap onto the bitmap of the three dimensional check box (should be '0' or '1', the 'gray' state is not supported).

picSrc is the name of the picture box containing the provided "3dboxch.bmp" picture (The picture should be accessible from anywhere in the project, preferably placed in the startup form or an invisible form that remains loaded at all times.).

picDest is the name of the picture box to receive the picture, Style% is the style of the option button.

Comments:

The bitmap covers the button portion of the option button control. This routine copies the appropriate bitmap onto the picture depending on the state of the actual option button. Use this routine in conjunction with the PassPicClickToOption routine, which passes mouse clicks on the bitmap onto the actual underlying check box. Style% will be one of the following: OPT_OFF_3D(193), OPT_ON_3D(217), OPT_OFF_DIAMOND(241) or OPT_ON_DIAMOND(265).

See Also:

PassPicClickToOption

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before DoOptionButton can be used.


EnumWins

assembler function contained in QPRO200.DLL

Purpose:

EnumWins returns the count of all top windows, children under a given window, all tasks, or all parent tasks.

Declare Syntax:

Declare Function EnumWins% Lib "QPRO200.DLL" (ByVal Flag%, Array%)

Call Syntax:

NWins = EnumWins%(Flag%, Array%())

Where:

Flag% specifies the type of window/task handles to count or return.  The base number specified below indicates the type of handles to count. Adding 1 to this value specifies that Array%() should be filled with the indicated handle types starting at element 1.  Array%(0)

can also be used on entry to specify a parent task or window as follows:

Flag Value*	Mode	Array% (0)on Entry	Array % (0) on Exit

 *2(3)	Get all window handles	N/A	N/A

 *4(5)	All children of parent hWnd	hWnd**	hWnd

 *8(9)	All task windows under

current task MDI only	N/A	hWnd

 *16(17)	Current task's window handles	N/A	hWnd

Comments:

There are many situations where a program needs to know the handles of all windows associated with a task (program).  You might want to hide all displayed windows when a main window such as a menu bar is minimized.  You can do this by calling Windows API

functions if you know the handles for each window.  You might also like to display a menu of tasks or windows for the user to switch to or close.

Example:

The following code counts the number of top level windows associated with the current task, and then fills an array with their handles.

NWins% = EnumWins%(16, Dummy%)

ReDim Handles%(NWins%)

NWins% = EnumWins%(17, Handles%(1))

EnumWins is demonstrated in MAIN.BAS, where it is used to update the list of open windows on the menu bar of QP4WIN.FRM.  It is also used to get a list of windows to be hidden or shown when the user minimizes or restores the QP4WIN.FRM form.

Example Location:

Project File	Form/BASIC File	Procedure

QP4WIN.MAK	MAIN.BAS	UpdateWinTitles

QP4WIN.MAK	SHOWHIDE.BAS	Show_Hide_All


File2CB and File2List

assembler routines contained in QPRO200.DLL

Purpose:

File2List and File2CB fill a List or Combo box in one operation from a specified sequential file.  The routines turn off the display while filling, and refresh it when completed to minimize screen flicker.

Declare Syntax:

Declare Sub File2CB Lib "QPRO200.DLL" (Combo As Control, ByVal _

FileName$)

Declare Sub File2List Lib "QPRO200.DLL" (List As Control, ByVal _

FileName$)

Call Syntax:

File2CB ComboName, FileName$

File2List ListName, FileName$

Where:

ComboName and ListName are the name of a combo box or list box control.  FileName$ is the name of the file from which to retrieve the list data.

Comments:

File2CB and File2List use the AddString message to fill their respective controls.  If you want to replace the contents in a List or Combo box you must clear the box first.  Subsequent calls to File2CB and File2List will continue to add items.

If the List or Combo box runs out of memory then File2CB and File2List will fill only what they can.

Note that File2List can also be used with the QPList custom list box control.

Also See:

Also see Array2CB and Array2List which fill a combo or list box from a string array.


FillFormClientArea

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

This routine is used to fill a form's client area with a bitmap built from an 8x8 pixel grid built at runtime.  You can either use one of several pre-defined patterns or use a custom pattern.

Syntax:

FillFormClientArea frmDest, Pattern%, fClr&, frmHeight%, frmWidth%

Where:

frmDest is the form to fill.

Pattern% is the pattern to use.

fClr& is the foreground color.

frmHeight% is the height, in pixels, of the fill.

frmWidth% is the width, in pixels, of the fill.

Comments:

The Autoredraw property of the form must be set to False. The Pattern% will be one of the following: PAT_ZIGZAG(1), PAT_DIAMONDS(4), PAT_BRICKS(2), PAT_LIGHTGRAY(3), or PAT_CUSTOM(99).

To use a custom pattern, first call the helper routine FillFXBitArray 8 times to establish the custom bit pattern, before calling FillFormClientArea.

  FillFXBitArray 0, &HFF

  FillFXBitArray 1, &HAA	'etc for 2 through 7

For resizeable forms, you should simply fill to the entire size of the screen.

See Also:

The BitmapBrush property of the CSForm control.

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before FillFormClientArea can be used.


FillPicture

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

This routine is used to fill a picture with a bitmap built from an 8x8 pixel grid built at runtime.   You can either use one of several pre-defined patterns or use a custom pattern.

Syntax:

FillPicture picDest, Pattern%, fClr&, HeightPercent%, WidthPercent%

Where:

picDest is the form to fill, Pattern% is the pattern to use.

fClr& is the foreground color.

HeightPercent% is the percentage of the picture control, vertically, that should be filled.

WidthPercent% is the percentage of the picture control, horizontally, that should be filled.

Comments:

The Autoredraw property of the form must be set to False.   The Pattern% will be one of the following: PAT_ZIGZAG(1), PAT_DIAMONDS(4),

PAT_BRICKS(2), PAT_LIGHTGRAY(3), or PAT_CUSTOM(99).

To use a custom pattern, first call the helper routine FillFXBitArray 8 times to establish the custom bit pattern, before calling FillFormClientArea.

FillFXBitArray 0, &HFF

FillFXBitArray 1, &HAA	'etc for 2 through 7

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before FillPicture can be used.


FramePictureOnControl

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

FramePictureOnControl is used to simulate a three dimensional frame control when the control you are framing is placed within another control, usually a picture box.

Syntax:

FramePictureOnControl Ctl, ctlContainer, Dist%, BoxEffect%, _

LineWidth%, FrameText$, LabelEffect%, Alignment%

Where:

Ctl is the control to be framed.

ctlContainer is the name of the control containing the framed control, Dist% is the offset, in pixels, of the frame from the control.

BoxEffect% is the style of the box border.

LineWidth% is the width of the framing line.

FrameText$ is the optional title text.

LabelEffect% is the text effect of the title text, and Alignment specifies the alignment of the text in the title.

Comments:

6 or 7 pixels is usually a good value for Dist%.

BoxEffect% will be one of the following: BOX_RAISED(0), BOX_SUNKEN(1), BOX_RAISEDLINE(3), BOX_SUNKENLINE(2) or BOX_SHADOWED(4).

LabelEffect% will be one of the following: LBL_STANDARD(0), LBL_RAISED(1),

LBL_SUNKEN(2).

Alignment will be one of the following: LBL_LEFT(0), LBL_CENTER(1), LBL_RIGHT(2).

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before FramePictureOnControl can be used.


FramePictureOnForm

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

FramePictureOnControl is used to simulate a three dimensional frame control when the control you are framing is placed directly on a form.

Syntax:

FramePictureOnForm Ctl, frmContainer, Dist%, BoxEffect%, _

LineWidth%, FrameText$, LabelEffect%, Alignment%

Where:

Ctl is the control to be framed.

frmContainer is the name of the form containing the framed control.

Dist% is the offset, in pixels, of the frame from the control.

BoxEffect% is the style of the box border.

LineWidth% is the width of the framing line.

FrameText$ is the optional title text.

LabelEffect% is the text effect of the title text.

Alignment specifies the alignment of the text in the title.

Comments:

6 or 7 pixels is usually a good value for Dist%.

BoxEffect% will be one of the following: BOX_RAISED(0), BOX_SUNKEN(1), BOX_RAISEDLINE(3), BOX_SUNKENLINE(2) or BOX_SHADOWED(4).

LabelEffect% will be one of the following: LBL_STANDARD(0), LBL_RAISED(1),

LBL_SUNKEN(2).

Alignment will be one of the following: LBL_LEFT(0), LBL_CENTER(1), LBL_RIGHT(2).

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before FramePictureOnForm can be used.


GenFrame

assembler subroutine contained in QPRO200.DLL

Purpose:

Paints a pop, drop or plain frame within an area of a window.

Declare Syntax:

Declare Sub GenFrame Lib "QPRO200.DLL" (FrRect as Rect, ByVal _

hDC%,ByVal FrameEffect%, ByVal SunColor&, ByVal ShadowColor&, _

ByVal BorderWidth%, ByVal FrameStyle%)

Call Syntax:

GenFrame FrRect, Form1.hDC, FrameEffect%, SunColor&, _

ShadowColor&,BorderWidth%, FrameStyle%

Where:

FrRect is an integer array or a type variable in the standard RECT structure format, Top, Left, Bottom, Right.

Type RECT

 X1 As Integer

 Y1 As Integer

 X2 As Integer

 Y2 As Integer

End Type

FrRect uses ONLY pixels for scale mode.

hDC is the a handle to a device context.  Normally this would be set to the Form's hDC property when used to draw a frame on a form.

FrameEffect tells CtlFrame which effect (off, pop, drop, plain) to use in painting the frame.

0 = None

1 = Pop

2 = Drop

3 = Plain

SunColor is a long integer specifying the color of the highlight side on a Pop or drop frame, and the overall color of a plain frame.  It can be set to any standard Visual Basic color value as a long integer.

ShadowColor is a long integer specifying the color of the shadow side of a Pop or Drop frame.  It can be set to any standard Visual Basic color value as a long integer.

BevelWidth specifies the width of a Pop, Drop, or Plain frame in pixels.

BorderStyle specifies whether a black line is painted inside the specified frame.

0 = None

1 = Fixed Single

Comments:

GenFrame is the master frame routine used by the CurTime and CSMeter controls and the CtlFrame subroutine.  It works only in Pixel mode, but will draw on anything.  GenFrame is best placed in the paint event of its parent to guarantee that the frame gets repainted if the form is moved or changed.

TIP:  If the form's AutoRedraw property is TRUE, you should instead put calls to GenFrame in the form's Load Event as the Paint event is never called in this case.


GetSystemResources

assembler function contained in QPRO200.DLL

Purpose:

GetSystemResources returns the percentage of free Windows system resources.

Declare Syntax:

Declare Function GetSystemResources% Lib "QPRO200.DLL" ()

Call Syntax:

FreeResources% = GetSystemResources%()

Where:

FreeResources% receives a whole number percentage of available system resources.

Comments:

There are many situations where you might want to know how many resources are available to your program.  The most common use would be to check that a certain percentage is free before loading and displaying a form.  Each control on a form consumes resources, and if there aren't many left Windows may not be able to re-paint forms properly or even display your form at all.

See Also:

Fre&

Example Location:

Project File	Form/BASIC File	Procedure

SYSINFO.MAK	SYSINFO.FRM	Form_Load


hCtl2Ndx

assembler function contained in QPRO200.DLL

Purpose:

hCtl2Ndx returns an index into the Visual Basic Controls collection object given a control.

Declare Syntax:

Declare Function hCtl2Ndx% Lib "QPRO200.DLL" (CtlName As Control)

Call Syntax:

CtlIndex% = hCtl2Ndx%(CtlName)

Where:

CtlName is the name of a control.

Returns:

The function returns an index into the Controls collection object's list of controls.

Comments:

While it may seem redundant to determine the Controls index given a control name (ie. Controls(hCtl2Ndx(MyCtl)).BackColor = &H0000FF), there may be situations where you need this information for controlling loops, etc. as follows:

For I = hCtl2Ndx%(FirstCtl) to hCtl2Ndx%(LastCtl)

  Controls(I).BackColor = &H0000FF

Next

The above code would paint all controls between FirstCtl and LastCtl red.

See Also:

hWnd2Ndx


hWnd2Ndx

assembler function contained in QPRO200.DLL

Purpose:

hWnd2Ndx returns an index into the Visual Basic Controls collection object given a control's window handle.

Declare Syntax:

Declare Function hWnd2Ndx% Lib "QPRO200.DLL" (ByVal hWnd%)

Call Syntax:

CtlIndex% = hWnd2Ndx%(hWnd%)

Where:

hWnd is a Visual Basic control's window handle.

Returns:

The function returns an index into the Controls collection object's list of controls if the given handle is a valid Visual Basic control (not a form), otherwise it returns 0.

Comments:

It is often necessary to determine the control that belongs to a window handle.  If for instance you wanted to change the color of a control as the user moves the cursor over it, you could use the CSForm's MouseEnter event along with the HWnd2Ndx function as follows:

Sub CSForm1_MouseEnter (Wnd As Integer, CtlName As

String, CtlTag As String)

   Static PrevWnd%

   Dim Ndx%

   '--- If moving over a new control

   If Wnd  PrevWnd% Then

      '--- Get the index of the new control

      Ndx% = hWnd2Ndx%(Wnd)

      '--- If it's 0, the mouse is over the form.

      If Ndx% Then

         '--- Paint the new control red

         Controls(Ndx%).BackColor = &HFF

         '--- Un-paint the previous control

         If PrevWnd% Then

            Controls(hWnd2Ndx%(PrevWnd%)).BackColor = &HFFFFFF

          End If

         PrevWnd% = Wnd

      End If

   End If

End Sub

See Also:

hCtl2Ndx


Icon2Cursor

Basic Function contained in ICON2CUR.BAS

Purpose:

Icon2Cursor will convert an icon stored in a Picture Box (not an image control) and convert it to a valid cursor handle.

Syntax:

hCursor% = Icon2Cursor (Picture1, X%, Y%)

Where:

Picture1 is the name of a picture box containing an icon (not a bitmap), X is the horizontal coordinate of the cursor hotspot and Y is the vertical coordinate of the cursor hotspot.

Comments:

To use this function, use File/Add menu commands to add the ICON2CUR.BAS module to your project.

The values for X and Y will logically depend on the nature of the cursor, although both of these values will often be zero. Please note, cursors should be destroyed when they are not in use as they consume system resources. Use the DestroyCursor function found in the ICON2CUR.BAS module.

The following code changes a Button control's cursor to an icon stored in a picture box and then safely destroys the cursor when the form is unloaded:

Cursor%

  Sub Form_Load ()

    hCursor% = Icon2Cursor(Picture1, X%, Y%)

    CSForm.ControlCursor(Btn.hWnd) = hCursor%

End Sub

  Sub Form_Unload ()

    DestroyCursor hCursor%

End Sub

Also See:

ControlCursor, CustomCursor, FormCursor

Example Location:

Project File	Form/BASIC File	Procedure

ICO2CUR.MAK	ICON2CUR.FRM	IconPict_Click


Initialize3DFX

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

Initialize3DFX needs to be called at the beginning of any application using the routines in #DFX.BAS. It sets up global variables and speeds up translations at call-time.

Syntax:

Initialize3DFX

Example Location:

Project File

3DFXDEMO.MAK


LBFindString

assembler function contained in QPRO200.DLL

Purpose:

Searches a list box (Visual Basic or the QPList custom control) for a matching string and returns the index of the match.

Declare Syntax:

Declare Function LBFindString% Lib "QPPRO200.DLL" (List As Control,_

 ByVal Start%, ByVal Search$, ByVal Selection%)

Call Syntax:

Found% = LBFindString%(CtrlName, Start%, Search$, Selection%)

Where:

CtrlName is the name of the list box you wish to search.  Start% is the starting index from which to start the search.  Use -1 to indicate that the search should start from the beginning of the list.

Search$ is the string or substring to search for, and since searching is not case sensitive you don't have to worry about its capitalization.  Select% can be set on entry to either True (-1) or False (0) to specify whether or not the index that is found should become the current selection.  This applies only to Single Select list boxes (see the Selection property of the QPList custom control documented in the "Custom Controls" section of this manual).

Found% is the result of the search, and it reflects the index number of the first match or -1 to indicate that no match was found.

Comments:

Although the QuickPak Professional for Windows QPList custom control has searching capabilities built in, FindString will do it much more quickly.  In addition, FindString can be used to search standard Visual Basic List Box controls.


LBGetSelCount

assembler function contained in QPRO200.DLL

Purpose:

Returns the number of selected (highlighted) items in a list box control.

Declare Syntax:

Declare Function LBGetSelCount% Lib "QPRO200.DLL" (Ctrl As Control)

Call Syntax:

Count% = LBGetSelCount%(ControlName)

Where:

ControlName is the name of a List Box control.

Count% returns the number of selected items.


LBGetSelection

assembler subroutine contained in QPRO200.DLL

Purpose:

LBGetSelection fills an integer array with the index values of the currently selected items in a multi-select list box.

Declare Syntax:

Declare Sub LBGetSelection Lib "QPRO200.DLL" (Ctrl as Control, Array%,  _

ByVal NumEls%)

Call Syntax:

LBGetSelection CtrlName, Array%(Start%), NumEls%

Where:

CtrlName is the name of the list box you wish to search.  Array%() is an integer array dimensioned to the maximum number of selections expected in the list box.  Use the QPList custom control SelCount property to determine the number of selected items.

Start% specifies where in the array you want the selection indexes to be placed.  NumEls% is number of subsequent elements in Array%().

Comments:

The LBSelection routine is intended only for use with multi-select list boxes created using the QuickPak Professional for Windows QPList custom control.  Please see the documentation for QPList contained in the "Custom Controls" section of the manual.

Although you could step through the list box checking the Selected property of each item, this method is much more efficient.

The following is an example of calling GetSelection:

TItems% = QPList1.SelCount

ReDim ItemArr%(1 To TItems%)

LBGetSelection QPList1, ItemArr%(1), TItems%

Here, you would first determine how many selections have been made by using the SelCount property of the List Box.  Next, you dimension an integer array to that number.  Finally, you call GetSelections passing the name of the List Box control and the array to be filled along with the number of elements contained in it.

ItemArr() is returned holding the index numbers of all selected items.


LBGetTopIndex

assembler function contained in QPRO200.DLL

Purpose:

Returns the index of the item at the top of the list window in a List Box control.

Declare Syntax:

Declare Function LBGetTopIndex% Lib "QPRO200.DLL" (Ctrl As Control)

Call Syntax:

Idx% = LBGetTopIndex%(ControlName)

Where:

ControlName is the name of a List Box control.

Idx% returns the top item's index.


LBResetContent

assembler subroutine contained in QPRO200.DLL

Purpose:

LBResetContent clears a List Box control.

Declare Syntax:

Declare Sub LBResetContent Lib "QPRO200.DLL" (Ctrl As Control)

Call Syntax:

Call LBResetContent(ControlName)

Where:

ControlName is the name of a List Box control.


LBSetTabStops

assembler subroutine contained in QPRO200.DLL

Purpose:

LBSetTabStops sets the position of tab stops within a List Box control (Visual Basic or the QPList custom control).

Declare Syntax:

Declare Sub LBSetTabStops Lib "QPRO200.DLL" (Ctrl As Control, _

Array&, ByVal NumEls%)

Call Syntax:

LBSetTabStops CtrlName, Array&(Start%), NumEls%

Where:

CtrlName is the name of the list box you wish to search.  Array&() is a long integer array whose elements specify the position of each tab stop in units of Twips.  NumEls% specifies the number of elements contained in the array.

Comments:

If you need to present list box data in a columnar format, you can use this routine to specify the location of each column.  Each element of the array you pass specifies the position in Twips from the left edge of the control to each column.  When you add data to the list box you can use the Chr$(9) Tab character to delimit the columns.

The following example fills a List Box with names and addresses from a Type array.  Each field of data is displayed in a separate column:

 ReDim Array&(1 To 5)

 Test$ = String$(Len(Cus(1).CName), "W")

 Array&(1) = TextWidth(Test$)

 Test$ = String$(Len(Cus(1).Company), "W")

 Array&(2) = Array&(1) + TextWidth(Test$)

 Test$ = String$(Len(Cus(1).Addr), "W")

 Array&(3) = Array&(2) + TextWidth(Test$)

 Test$ = String$(Len(Cus(1).City), "W")

 Array&(4) = Array&(3) + TextWidth(Test$)

 Test$ = String$(Len(Cus(1).State), "W")

 Array&(5) = Array&(4) + TextWidth(Test$)

 LBSetTabStops List1, Array&(1), 5

 For N = 1 to Customers

 X$ = Cus(N).LName

 X$ = X$ + Chr$(9) + Cus(N).FName

 X$ = X$ + Chr$(9) + Cus(N).Company

 X$ = X$ + Chr$(9) + Cus(N).Addr

 X$ = X$ + Chr$(9) + Cus(N).City

 X$ = X$ + Chr$(9) + Cus(N).State

 X$ = X$ + Chr$(9) + Cus(N).Zip

 List1.AddItem X$

 Next

First, Array$() is dimensioned to the number of tab stops required. Next, the position of each tab stop is computed based on the width of the field before it.  This is done using the Visual Basic TextWidth method.  TextWidth returns the width of a string based on the current font

in Twips.  Here, you create a string of "W"s the same length as the previous field, and compute its display width.  You use an "W" because it is one of the widest characters in any font, which ensures that the column will be wide enough to display the longest string.

After calling LBSetTabStops, each field of the Type array is added to a temporary string separated by a Tab character.  Finally, the temporary string is added to the List Box using the Visual Basic AddItem method.

If you want to use a single column width for all columns, you can pass a long integer to LBSetTabStops in place of an array, and set the NumEls% parameter to 1.  The following example specifies that all data separated by Tab characters should be displayed 1000 Twips

apart:

 LBSetTabStops CtrlName, 1000, 1

To reset the tab spacing to the default value of eight times the average character width in the current font, use a long integer value of 0 instead of the array, and specify 0 for the number of elements NumEls%:

 LBSetTabStops CtrlName, 0, 0


LineOnForm

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

The LineOnForm routine is used to draw etched or raised lines on forms based on pixel coordinates.

Syntax:

LineOnForm Frm, Effect$, lWidth%, X1, Y1, X2, Y2

Where:

Frm is the form on which to draw.

Effect% is the line effect.

lWidth% is the width of the line.

X1 is the starting X position of the line.

Y1 is the starting Y position of the line.

X2 is the ending X position of the line.

Y2 is the ending Y position of the line.

Comments:

The pixel count is based on screens with 15 twips per pixel. The pixel count is automatically adjusted for monitors that have 12 twips per pixel.

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before LineOnForm can be used.


LineOnPicture

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

LineOnPicture This routine is used to draw etched or raised lines on picture controls based on pixel coordinates.

Syntax:

LineOnForm picDest, Effect$, lWidth%, X1, Y1, X2, Y2

Where:

picDest is the picture control on which to draw.

Effect% is the line effect.

lWidth% is the width of the line.

X1 is the starting X position of the line.

Y1 is the starting Y position of the line.

X2 is the ending X position of the line.

Y2 is the ending Y position of the line.

Comments:

The pixel count is based on screens with 15 twips per pixel. The pixel count is automatically adjusted for monitors that have 12 twips per pixel.

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before LineOnPicture can be used.


LoadBmp

Visual Basic Function contained in BITMAP.BAS

Purpose:

LoadBmp loads a monochrome, 16-color, 256-color, or 24-bit color BMP file into a BMPTYPE structure so that it can be displayed or manipulated.

Syntax:

Call LoadBmp (FileName$, Bmp)

Where:

FileName$ is the complete path and filename of the BMP file, and Bmp is a type variable of type BMPTYPE

Comments:

The BMPTYPE structure is as follows:

Type BMPTYPE

File As BITMAPFILEHEADER 	'-- 14 Bytes

Info As BITMAPINFOHEADER 	'-- 40 Bytes

BInfo As BITMAPINFO 	'-- 1064 Bytes

hBitmap As Integer 	'-- Handle to the bitmap

hPalette As Integer 	'-- Handle to the palette

FileName As String * 50	'-- BMP file name (for your use)

hWnd As Integer  	'-- Handle to display window

Top As Integer  	'-- Top pixel position inside window

Left As Integer  	'-- Left pixel position inside window

ZoomFactor As Single 	'-- Zoom In = 1, Zoom Out = 0  and  1

End Type

All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types. Make sure to paste the BITMAP.GLO file in your global module. Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBmp function before ending your program, or the bitmap will stay in memory. A good idea is to place DeleteBmp calls in your Form_Unload event procedure.

Example:

The following example will load and display a bitmap file on Form1

Dim Bmp as BmpType

FileName$ = "C:\WINDOWS\256COLOR.BMP"

Call LoadBmp(FileName$, Bmp)

Bmp.hWnd = Form1.hWnd

Call DisplayBmp(Bmp)

Example Location:

Project File	Form/BASIC File	Procedure

DEMOBMP.MAK	DEMOBMP.BAS	GetAndDisplay


ModeInfo

assembler function contained in QPRO200.DLL

Purpose:

Returns a string defining the current Windows operating mode.

Syntax:

Declare Function ModeInfo$ Lib "QPRO200.DLL" ()

Where:

OperatingMode$ receives one of the following strings:

"Standard Mode" or

"Enhanced mode".

Comments:

You can use this function to determine or display the current Windows operating mode.  This is useful when you want to know if it is possible to use Windows features that are supported in enhanced (386) mode.  While you could detect the presence of a 386 processor, Windows itself may not be operating in Enhanced mode.

Example Location:

Project File	Form/BASIC File	Procedure

SYSINFO.MAK	SYSINFO.FRM	Form_Load


MonoPrint

assembler subroutine contained in QPRO200.DLL

Purpose:

MonoPrint lets you print debug or other data to a second monochrome monitor.

Declare Syntax:

Declare Sub MonoPrint Lib "QPRO200.DLL" (Text$, ByVal Row%, _

ByVal Col%, ByVal Attrib%)

Call Syntax:

MonoPrint Text$, Row%, Col%, Attrib%

Where:

Text$ is the text to be displayed, Row% and Col% are the row and column to display at, and Attrib% is the color to use.

Comments:

If your system has a monochrome as well as a color graphics video card installed, you can use this routine to display data on the monochrome monitor instead of the Windows display.  This can be very handy for displaying debugging information without affecting a program's Windows display.  Also, many data acquisition programs require very fast screen updates of live data that may exceed the capabilities of a Windows Display.


PaintFrameCtrl

BASIC Subprogram contained in FRAMES.BAS

Purpose:

PaintFrameCtrl draws a concave frame around a control within a Form.

Syntax:

PaintFrameCtrl FormName, CtrlName

Where:

FormName is the name of the form on which to draw the frame, and  CtrlName is the name

of the control around which to draw the frame.

Comments:

This routine is similar to DepthFrameFrm except you pass it the name of a control rather than a type structure.  While this is much simpler than filling in a Type variable with coordinates and colors, it is also less flexible.

The colors for the sides are fixed at grey for the top and left, and white for the bottom and right sides.  The thickness for the lines is fixed at 15 Twips.  Typically you would call this routine from a Form's Paint procedure, if its AutoRedraw property is set to False.  If AutoRedraw is True, the call should instead be placed in the Form's Load procedure, since the Paint procedure is never invoked in this case.

See Also:

DepthFrameCtrl DepthFrameFrm and PaintFrameFrm

Example Location:

DepthFrameFrm is used in the Form_Paint procedures of most of the supplied demonstration forms.


PaintFrameFrm

BASIC Subprogram contained in FRAMES.BAS

Purpose:

PaintFrameFrm draws a convex frame around a Form.  This gives the  forms an attractive appearance.

Syntax:

PaintFrameFrm FormName

Where:

FormName is the name of the form around which to draw the frame.

Comments:

This routine is similar to DepthFrameFrm except you pass it the name of a form rather than a type structure.  While this is much simpler than filling in a Type variable with coordinates and colors, it is also less flexible.

The colors for the sides are fixed at grey for the top and left, and white for the bottom and right sides. The thickness for the lines is fixed at 15 Twips.

Typically you would call this routine from a Form's Paint procedure, if its AutoRedraw property is set to False.  If AutoRedraw is True the call should instead be placed in the Form's Load procedure since the Paint procedure is never invoked in this case.  If you do

call it from your paint procedure make sure the form is not minimized, because its size is undefined under this condition.  For example:

Sub Form_Paint ()	'If the form is not minimized (1),

   If WindowState <<>> MINIMIZED Then	'  draw the frame.

      PaintFrameFrm MyForm

   End If

End Sub

See Also:

DepthFrameCtrl DepthFrameFrm and PaintFrameFrm

Example Location:

DepthFrameFrm is used in the Form_Paint procedures of most of the supplied demonstration forms.


PassPicClickToCheck

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

PassPicClickToCheck passes mouse clicks from a bitmap on to an underlying checkbox.

Syntax:

PassPicClickToCheck chkDest

Where:

chkDest is the name of the check box to trigger.

Comments:

Call this routine from the Click event of the bitmap which is associated with an actual Check Box. When the bitmap is clicked, this routine simply manually changes the value of the real Check Box. This in turn causes the Click event of the real Check Box to trigger as it would if

the user had clicked on it instead of the bitmap.

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before PassPicClickToCheck can be used.


PassPicClickToOption

Visual Basic subroutine contained in 3DFX.BAS

Purpose:

PassPicClickToOption passes a mouse click to an option button underlying a bitmap when using the 3DFX routines.

Syntax:

PassPicClickToOption optDest

Where:

optDest is the name of the option button to which the mouse click is to be passed.

Comments:

Call this routine from the Click event of the bitmap which is associated with an actual Option Button. When the bitmap is clicked, this routine simply manually changes the value of the real Option Button. This in turn causes the Click event of the real Option Button to trigger as it would if the user had clicked on it instead of the bitmap.

Example Location:

Project File

3DFXDEMO.MAK

NOTE

The Initialize3DFX routine must be called before PassPicClickToOption can be used.


PrintBmp

Visual Basic Subroutine contained in BITMAP.BAS

Purpose:

Prints a BMP file on the default Windows Printer.

Syntax:

Call PrintBmp (Bmp, ScaleX, ScaleY, PrintLeft, PrintTop)

Where:

Bmp is a BMPTYPE type variable that contains the bitmap information.

ScaleX is the scale factor for the width of the printed image (1 = normal size).

ScaleY is the scale factor for the height of the printed image (1 = normal size).

PrintLeft specifies the left position (in pixels) of the printed image.

PrintTop specifies the top position (in pixels) of the printed image.

Comments:

The BMPTYPE structure is as follows:

Type BMPTYPE

 File As BITMAPFILEHEADER 	'-- 14 Bytes

 Info As BITMAPINFOHEADER 	'-- 40 Bytes

 BInfo As BITMAPINFO 	'-- 1064 Bytes

 hBitmap As Integer 	'-- Handle to the bitmap

 hPalette As Integer 	'-- Handle to the palette

 FileName As String * 50 	'-- BMP file name (for your use)

 hWnd As Integer  	'-- Handle to display window

 Top As Integer  	'-- Top pixel position inside window

 Left As Integer  	'-- Left pixel position inside window

 ZoomFactor As Single 	'-- Zoom In = 1, Zoom Out = 0  and 1

End Type

All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types.   Make sure to paste the BITMAP.GLO file in your global module. Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBmp function before ending your program, or the bitmap will stay in  memory. A good idea is to place DeleteBmp calls in your Form_Unload event procedure.

Example:

The following example will Capture the client area of Form1 to a BMPTYPE variable, print it with a scale factor of 4, and save it to a file called CAPTURE.BMP:

 Dim Cap As BmpType

 Dim Area As Rect

 hw = Form1.hWnd

 Call CaptureBmp(hW, Area, Cap)

 ScaleX = 4

 ScaleY = 4

 PrintLeft = 1

 PrintTop = 1

 Call PrintBmp(Cap, ScaleX, ScaleY, PrintLeft, PrintTop)

 Call SaveBmp("CAPTURE.BMP", Cap)

 Call DeleteBmp(Bmp)


SaveBmp

Visual Basic Subroutine contained in BITMAP.BAS

Purpose:

SaveBmp writes a loaded bitmap to a BMP file.

Syntax:

Call SaveBmp (FileName$, Bmp, SaveFormat)

Where:

FileName$ is the complete path and filename of the BMP file, and Bmp is a BMPTYPE type variable that contains the bitmap information SaveFormat specifies the color resolution of the BMP file.

0 = same as video screen

1 = mono

2 = 16-Color

3 = 256 Color

4 = 24-Bit

Comments:

The BMPTYPE structure is as follows:

Type BMPTYPE

 File As BITMAPFILEHEADER 	'-- 14 Bytes

 Info As BITMAPINFOHEADER 	'-- 40 Bytes

 BInfo As BITMAPINFO 	'-- 1064 Bytes

 hBitmap As Integer 	'-- Handle to the bitmap

 hPalette As Integer 	'-- Handle to the palette

 FileName As String * 50 	'-- BMP file name (for your use)

 hWnd As Integer  	'-- Handle to display window

 Top As Integer  	'-- Top pixel position inside window

 Left As Integer  	'-- Left pixel position inside window

 ZoomFactor As Single 	'-- Zoom In = 1, Zoom Out = 0  and 1

End Type

All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types. Make sure to paste the BITMAP.GLO file in your global module. Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBmp function before ending your program, or the bitmap will stay in  memory. A good idea is to place DeleteBmp calls in your Form_Unload event procedure.

Example:

The following example will Capture the client area of Form1 to a BMPTYPE variable and save it to a file called CAPTURE.BMP using the current screen mode:

 Dim Bmp As BmpType

 Dim Area As Rect

 hw = Form1.hWnd

 Call CaptureBmp(hW, Area, Bmp)

 Call SaveBmp("CAPTURE.BMP", Bmp, 0)

 Call DeleteBmp(Bmp)


SaveCSPicture

Visual Basic Subroutine contained in SAVEPICT.BAS

Purpose:

SaveCSPicture writes an image in a CSPicture control to a BMP file.

Syntax:

Call SaveCSPicture (CSPicture, FileName$, SaveFormat)

Where:

CSPicture is the name of a CSPicture control containing the image.

FileName$ is the complete path and filename of the BMP file.

SaveFormat specifies the color resolution of the BMP file.

 0 = same as video screen

 1 = mono

 2 = 16-Color

 3 = 256 Color

 4 = 24-Bit

Comments:

SaveCSPicture requires BITMAP.BAS to be loaded into your project.

Example:

The following example will save the contents of CSPicture1 to "TEST.BMP" using the

current screen mode:

 Call SaveCSPicture(CSPicture1, "CAPTURE.BMP", 0)


SelectRect

Visual Basic Subroutine contained in BITMAP.BAS

Purpose:

SelectRect allows the user to select a rectangle area of the screen by clicking and dragging the mouse.

Syntax:

Call SelectRect (Area)

Where:

Area is a variable of type RECT.

Comments:

This is a useful routine for screen capture, because it allows you to select any area of the screen. The rectangle is returned to you in the RECT structure, which you can pass to CaptureBmp, which captures that area of the screen into a BMPTYPE variable.  The Area variable must be dimensioned as a RECT structure, as follows:

Type RECT

 Left As Integer

 Top As Integer

 Right As Integer

 Bottom As Integer

End Type

Dim Area As Rect


SetBmpData

Visual Basic Subroutine contained in BITMAP.BAS

Purpose:

SetBmpData creates a BMPTYPE variable given an hBitmap and hPalette.

Syntax:

Call SetBmpData (hBitmap, hPalette, Bmp)

Where:

hBitmap is a valid handle to a bitmap.

hPalette is a valid handle to a palette.

Bmp is a BMPTYPE type variable that contains the bitmap information

Comments:

SetBmpData is useful if you have a valid bitmap handle and palette handle, and you want to use them with the Bitmap routines. It fills in the bitmap headers and palette data so the BMPTYPE variable can be used.  The BMPTYPE structure is as follows:

Type BMPTYPE

 File As BITMAPFILEHEADER 	'-- 14 Bytes

 Info As BITMAPINFOHEADER 	'-- 40 Bytes

 BInfo As BITMAPINFO 	'-- 1064 Bytes

 hBitmap As Integer 	'-- Handle to the bitmap

 hPalette As Integer 	'-- Handle to the palette

 FileName As String * 50 	'-- BMP file name (for your use)

 hWnd As Integer  	'-- Handle to display window

 Top As Integer  	'-- Top pixel position inside window

 Left As Integer  	'-- Left pixel position inside window

 ZoomFactor As Single 	'-- Zoom In = 1, Zoom Out = 0  and 1

End Type

All of the bitmap routines use QuickPak Professional DLL routines as well as many Windows API functions and data types. Make sure to paste the BITMAP.GLO file in your global module. Since you may already be using some of the functions declared in BITMAP.GLO, it is advisable to construct a new global module, adding only the declarations that are not already in use.

NOTE:

You must delete every bitmap you load or capture with the DeleteBmp function before ending your program, or the bitmap will stay in  memory. A good idea is to place DeleteBmp calls in your Form_Unload event procedure.

Example:

The following example will Capture the client area of Form1 to a BMPTYPE variable and save it to a file called CAPTURE.BMP:

 Dim Bmp As BmpType

 Dim Area As Rect

 hw = Form1.hWnd

 Call CaptureBmp(hW, Area, Bmp)

 Call SaveBmp("CAPTURE.BMP", Bmp)

 Call DeleteBmp(Bmp)


SplitColor

assembler subroutine contained in QPRO200.DLL

Purpose:

SplitColor is used to split a Windows long integer color value into its component RGB (Red, Green, and Blue) values.

Declare Syntax:

Declare Sub SplitColor Lib "QPRO200.DLL" (Red%, Green%, Blue%, _

CombColr&)

Call Syntax:

SplitColor Red%, Green%, Blue%, CombColor&

Where:

Red%, Green%, and Blue% receive the component values of the combined CombColor& color value.

Comments:

This routine is meant to complement the Visual Basic RGB function, where individual component colors are combined to form a Windows color value.

Example Location:

Project File	Form/BASIC File	Procedure

CLRCHART	CLREDIT.FRM	UpDateClr


SysDir

assembler function contained in QPRO200.DLL

Purpose:

SysDir returns the Windows drive and System directory name.

Declare Syntax:

Declare Function SysDir$ Lib "QPRO200.DLL" ()

Call Syntax:

SystemDirectory$ = SysDir$()

Where:

SystemDirectory$ receives the full path to Windows' system directory.

Comments:

Windows keeps several dynamic link libraries, device drivers, and configuration table files in a separate directory for its own use. If your program needs to detect the presence of one of these files, SysDir can supply the full path to it.

See Also:

WinDir

Example Location:

Project File	Form/BASIC File	Procedure

SYSINFO.MAK	SYSINFO.FRM	Form_Load


WinDir

assembler function contained in QPRO200.DLL

Purpose:

WinDir returns the Windows drive and directory name.

Declare Syntax:

Declare Function WinDir$ Lib "QPRO200.DLL" ()

Call Syntax:

WindowsDirectory$ = WinDir$()

Where:

WindowsDirectory$ receives the full path to Window's main directory in the form "C:\WIN3".

Comments:

Windows keeps most of its programs and setup information in its own directory for use by itself and other programs.  If your program needs to find a file in this directory, WinDir can supply the full path to it.

See Also:

SysDir$

Example Location:

Project File	Form/BASIC File	Procedure

SYSINFO.MAK	SYSINFO.FRM	Form_Load


WinVer

assembler function contained in QPRO200.DLL

Purpose:

WinVer returns the version number of Windows that is presently running on the host PC.

Declare Syntax:

Declare Function WinVer% Lib "QPRO200.DLL" ()

Call Syntax:

Version! = WinVer%() / 100

Major% = WinVer%() \ 100

Minor% = WinVer%() Mod 100

Where:

Version% receives the Windows version number.  The major and minor version components may also be determined as shown in the example above.

Comments:

As new versions of Windows_with new capabilities_become available, it will become increasingly important for a program to know what version it is running under.  There will undoubtedly be easier ways of doing things such that your program would want to take advantage of them if possible, or revert to an older method if not available on the host system.

See Also:

DosVer%

Example Location:

Project File	Form/BASIC File	Procedure

SYSINFO.MAK	SYSINFO.FRM 	Form_Load


X/YDialog2Pixel

assembler function contained in QPRO200.DLL

Purpose:

XDialog2Pixel and YDialog2Pixel returns the number of Pixels contained in a given number of horizontal and  vertical Dialog Units.

Declare Syntax:

Declare Function XDialog2Pixel% Lib "QPRO200.DLL" (ByVal xDialog%)

Declare Function YDialog2Pixel% Lib "QPRO200.DLL" (ByVal yDialog%)

Call Syntax:

X% = XDialog2Pixel%(xDialog%)

Y% = YDialog2Pixel%(yDialog%)

Where:

X% and Y% receive the number of  pixels contained in a given number of horizontal(xDialog%) and vertical(yDialog%) dialog units respecitvely.

Comments:

Dialog boxes tend to use their own coordinate system that are based on the default font for that dialog box. Dialog units specify a cordinate system within a dialog box. Each horizontal dialog unit is one-quarter of the width of the dialog box font. Thus, if font is eight pixels wide,

the horizontal base unit will be two pixels. Each vertical dialog unit is one-eight of the height of the dialog box font.

See Also:

X/YPixel2Dialog X/YDialog2Twp X/YTwp2Dialog X/YPixel2Twp X/YTwp2Pixel


X/YPixel2Dialog

assembler function contained in QPRO200.DLL

Purpose:

XPixel2Dialog and YPixel2Dialog returns the number of Dialog Units contained in a given number of horizontal and  vertical Pixels.

Declare Syntax:

Declare Function XPixel2Dialog% Lib "QPRO200.DLL" (ByVal xPixel%)

Declare Function YPixel2Dialog% Lib "QPRO200.DLL" (ByVal yPixel%)

Call Syntax:

X% = XPixel2Dialog%(xPixel%)

Y% = YPixel2Dialog%(yPixel%)

Where:

X% and Y% receive the number of  dialog units  contained in a given number of horizontal(xPixel%) and vertical(yPixel%) pixels respecitvely.

Comments:

Dialog boxes tend to use their own coordinate system that are based on the default font for that dialog box. Dialog units specify a cordinate system within a dialog box. Each horizontal dialog unit is one-quarter of the width of the dialog box font. Thus, if font is eight pixels wide,

the horizontal base unit will be two pixels. Each vertical dialog unit is one-eight of the height of the dialog box font.

See Also:

X/YDialog2Pixel X/YDialog2Twp X/YTwp2Dialog X/YPixel2Twp X/YTwp2Pixel


X/YDialog2Twp

assembler function contained in QPRO200.DLL

Purpose:

XDialog2Twp and YDialog2Twp returns the number of Twips contained in a given number of horizontal and  vertical Dialog Units

Declare Syntax:

Declare Function XDialog2Twp& Lib "QPRO200.DLL" (ByVal xPixel%)

Declare Function YDialog2Twp& Lib "QPRO200.DLL" (ByVal yPixel%)

Call Syntax:

X& = XDialog2Twp&(xDialog%)

Y& = YDialog2Twp&(yDialog%)

Where:

X& and Y& receive the number of  Twips  contained in a given number of horizontal(xPixel%) and vertical(yPixel%) Dialog Units respecitvely.

Comments:

Dialog boxes tend to use their own coordinate system that are based on the default font for that dialog box. Dialog units specify a cordinate system within a dialog box. Each horizontal dialog unit is one-quarter of the width of the dialog box font. Thus, if font is eight pixels wide,

the horizontal base unit will be two pixels. Each vertical dialog unit is one-eight of the height of the dialog box font.

See Also:

X/YDialog2Pixel X/YPixel2Dialog X/YTwp2Dialog X/YPixel2Twp X/YTwp2Pixel


X/YTwp2Dialog

assembler function contained in QPRO200.DLL

Purpose:

XTwp2Dialog and YTwp2Dialog returns the number of Dialog Units contained in a given number of horizontal and  vertical Twips.

Declare Syntax:

Declare Function XTwp2Dialog% Lib "QPRO200.DLL" (ByVal xTwp&)

Declare Function YTwp&Dialog% Lib "QPRO200.DLL" (ByValyTwp&)

Call Syntax:

X% = XTwp2Dialog% (xTwip&)

Y% = YTwp2Dialog% (yTwip&)

Where:

X% and Y% receive the number of  Dialog Units  contained in a given number of horizontal(xTwip&) and vertical(yTwip&) Twips respecitvely.

Comments:

Dialog boxes tend to use their own coordinate system that are based on the default font for that dialog box. Dialog units specify a cordinate system within a dialog box. Each horizontal dialog unit is one-quarter of the width of the dialog box font. Thus, if font is eight pixels wide,

the horizontal base unit will be two pixels. Each vertical dialog unit is one-eight of the height of the dialog box font.

See Also:

X/YDialog2Pixel X/YPixel2Dialog X/YDialog2Twp X/YPixel2Twp X/YTwp2Pixel


X/YPixel2Twp

assembler functions contained in QPRO200.DLL

Purpose:

XPixel2Twp and XPixel2Twp return the number of Twips contained in a given number of pixels (logical units) in horizontal and vertical direction respectively.

Declare Syntax:

Declare Function XPixel2Twp& Lib "QPRO200.DLL" (ByVal xPixel%)

Declare Function YPixel2Twp& Lib "QPRO200.DLL" (ByVal yPixel%)

Call Syntax:

XTwips& = XPixel2Twp&(Pixels%)

YTwips& = YPixel2Twp&(Pixels%)

Where:

XTwips& and YTwips& receive the number of Twips contained within the given Pixel% value.

Comments:

It is often useful to know the number of Twips in a given number of pixels.  Most GDI routines use "Logical Units" or pixels to specify coordinates within a window for drawing or painting.  While you could use Pixels as the ScaleMode for your form, it is most often more convenient to use Twips, and simply make the conversion for each GDI call.

See Also:

X/YTwp2Pixel


X/YTwp2Pixel

assembler functions contained in QPRO200.DLL

Purpose:

XTwp2Pixel and XTwp2Pixel return the number of pixels (logical units) contained in a given number of Twips in the horizontal and vertical direction respectively.

Declare Syntax:

Declare Function XTwp2Pixel% Lib "QPRO200.DLL" (ByVal Twips&)

Declare Function YTwp2Pixel% Lib "QPRO200.DLL" (ByVal Twips&)

Call Syntax:

XPixels% = XTwp2Pixel%(Twips&)

YPixels% = YTwp2Pixel%(Twips&)

Where:

XPixels% and YPixels% receive the number of pixels contained within the given Twips& value.

Comments:

It is often useful to know the number of pixels in a given number of Twips.  Most GDI routines use "Logical Units" or pixels to specify coordinates within a window for drawing or painting. While you could use Pixels as the ScaleMode for your form, it is most often more convenient to use Twips, and simply make the conversion for each GDI call.

See Also:

X/YPixel2Twp